如何在Java 8中使用子类进行方法链接

问题描述:

我遵循this pattern实现了Java中的子类方法链接。我们的目标是,我有一个父类的方法,但可以分配的子类,如:如何在Java 8中使用子类进行方法链接

interface Screen { 
    <T extends Screen> T setBrightness(int value); 
    <T extends Screen> T setContrast(int value); 
} 

class CrtScreen implements Screen { 

    @SuppressWarnings("unchecked") 
    @Override 
    public <T extends Screen> T setBrightness(int value) { 
     // ... 
     return (T) this; 
    } 

    @SuppressWarnings("unchecked") 
    @Override 
    public <T extends Screen> T setContrast(int value) { 
     // ... 
     return (T) this; 
    } 
} 

class ColorCrt extends CrtScreen { /* ... */ } 

public static void main(String[] args) { 
    ColorCrt cc = new ColorCrt().setBrightness(50).setContrast(50); 
} 

现在我也有,我想我的对象添加到容器对象,如:

class Stuff { 
    Stuff add(Screen s) { 
     // ... 
     return this; 
    } 

    Stuff add(String s) { 
     // ... 
     return this; 
    } 

    Stuff add(int i) { 
     // ... 
     return this; 
    } 
} 

public static void main(String[] args) { 
    new Stuff().add(new ColorCrt().setBrightness(50).setContrast(50)).add(25).add("..."); 
} 

这现在不再适用于Java 8,给我方法add(Screen)对于类型Stuff不明确。我明白原因为explained here。我只看到两个选项的时刻:

  • 我不使用<T extends,只是Screen setBrightness(value)。它使我可以选择将我的实现类分配给相应的变量,并且当我想执行特定于实现的方法时,我必须将其转换。

  • 我必须在我的方法链接中添加强制类型或类型参数。这是非常难看 阅读并很难解决的大结构(拳击的多个级别),而必须从Java 7

移植有没有在Java中实现8让我方法链接的方式仍然有两个功能?如果没有,这两种方法中的一种可以被认为更有意义吗?

+1

什么'类屏幕 {'? –

+0

我不明白为什么'添加(屏幕)'应该是模棱两可的。 – lexicore

+0

@lexicore ...因为'String'的子类(假设'String'不会是'final')可能实现'interface Screen'。在编译时,编译并不知道运行时的对象是否可以是这样的子类。 (编译器不关心那个'String'实际上不能被继承,因为它是'final'。)参见:https://*.com/q/28466925/1503237 – Paramaeleon

在您的代码:

class CrtScreen implements Screen { 

    @SuppressWarnings("unchecked") 
    @Override 
    public <T extends Screen> T setBrightness(int value) { 
     // ... 
     return (T) this; 
    } 

    @SuppressWarnings("unchecked") 
    @Override 
    public <T extends Screen> T setContrast(int value) { 
     // ... 
     return (T) this; 
    } 
} 

你的成员函数setContrast和setBrightness不需要是通用的,如果它是通用的,你需要指定类型是什么(在调用点)。我用这个办法,而使得界面通用的,这意味着这些方法need'nt是:

interface Screen<T extends Screen> { 
    T setBrightness(int value); 
    T setContrast(int value); 
} 

class CrtScreen implements Screen<CrtScreen> { 
    @SuppressWarnings("unchecked") 
    @Override 
    public CrtScreen setBrightness(int value) { 
     // ... 
     return this; 
    } 

    @SuppressWarnings("unchecked") 
    @Override 
    public CrtScreen setContrast(int value) { 
     // ... 
     return this; 
    } 
} 

如果你想继续做你的方式,你就需要指定其类型适用于通用(见下图):

public static void main(String[] args) { 
    new Stuff() 
    .add(new ColorCrt().<ColorCrt>setBrightness(50)) 
    .add(25).add("..."); 
} 
+0

这正是我所描述的。您的第一个建议不适用于像ColorCrt cc = new ColorCrt()。setBrightness(50)这样的分配。setContrast(50);'-error是*类型不匹配:不能从CrtScreen转换为ColorCrt *。第二种方法有效,但写起来很难看。我正在寻找的是解决这两个问题的解决方案,允许分配子类*和*方法链*而不需要在任何地方指定类型。也许这需要一个完全不同的方法。 – Paramaeleon

+0

CrtScreen是ColorCrt的基础,因此Color ...可以转换(与多态兼容)到CrtScreen,但不是反过来 - 这是有目的的。转换为基础时可以执行分配,但不能派生。在这两种情况下,它们都与屏幕(全部基础)多态兼容,这正是您想要的。我不确定你想要做什么。也许你应该发布更多的代码,因为在我看来,我发布的代码已经足够了 –

+0

@Paramaeleon,除此之外,我认为在这种情况下你根本不需要泛型。您的基地应该通过其界面为您提供所有必需的功能,否则您会误解多态性。 –

最后我发现,通过使interfaceabstract class对我工作的解决方案。这似乎足以满足Java 8编译器的要求。

概念证明:

static abstract class Screen { 
    abstract <T extends Screen> T setBrightness(int value); 
    abstract <T extends Screen> T setContrast(int value); 
} 

static class CrtScreen extends Screen { 

    @SuppressWarnings("unchecked") 
    @Override 
    public <T extends Screen> T setBrightness(int value) { 
     // ... 
     return (T) this; 
    } 

    @SuppressWarnings("unchecked") 
    @Override 
    public <T extends Screen> T setContrast(int value) { 
     // ... 
     return (T) this; 
    } 
} 

static class ColorCrt extends CrtScreen { /* ... */ } 

static class Stuff { 
    Stuff add(Screen s) { 
     return this; 
    } 

    Stuff add(String s) { 
     return this; 
    } 

    Stuff add(int i) { 
     return this; 
    } 
} 

public static void main(String[] args) { 
    ColorCrt cc = new ColorCrt().setBrightness(50).setContrast(50); 
    new Stuff().add(new ColorCrt().setBrightness(50).setContrast(50)).add(25).add("..."); 
} 
+2

这是一个破碎的通用结构。只要考虑'ColorCrt cc = new CrtScreen()。setBrightness(0);',现在编译时没有任何错误,但显然会在运行时失败。这就是为什么你得到这些* unchecked *警告你压制。 – Holger

+0

为什么不依赖协变返回类型?例如,看看'StringBuilder'如何实现'Appendable'。 –