【问题标题】:How to do method chaining with subclasses in Java 8如何在 Java 8 中使用子类进行方法链接
【发布时间】:2018-03-21 16:19:10
【问题描述】:

我跟着this pattern 用Java 中的子类实现了方法链接。目标是我在超类上有一个方法,但可以分配子类,例如:

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。我目前只看到两个选项:

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

  • 我必须在方法链中添加强制类型转换或类型参数。这很丑 对于必须从 Java 7 移植的大型结构(许多级别的装箱),阅读起来很难修复。

有没有办法在 Java 8 中实现方法链接,以便我仍然拥有这两个功能?如果不是,是否可以认为这两种方法中的一种更有意义?

【问题讨论】:

  • class Screen&lt;T extends Screen&gt; { 呢?
  • 我不太明白为什么add(Screen) 应该是模棱两可的。
  • @lexicore ... 因为String 的子类(假设String 不是final)可能实现interface Screen。在编译时,编译器不知道运行时的对象是否可能是这样做的子类。 (编译器并不关心 String 实际上不能被子类化,因为它是 final。)见这里:stackoverflow.com/q/28466925/1503237
  • 鉴于 Java 支持协变返回类型,我认为根本没有理由使用泛型。 Screen 可以声明Screen setBrightness(int)CrtScreen 可以实现/覆盖该方法为CrtScreen setBrightness(int)。您的原始实现不是,也从来不是类型安全的。

标签: java generics java-8 method-chaining


【解决方案1】:

在您的代码中:

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 不需要是通用的,如果是通用的,您需要指定类型是什么(在调用站点)。我使用了这种方法,而是使接口通用,这意味着方法不需要:

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("...");
}

【讨论】:

  • 这正是我所描述的。您的第一个建议不适用于ColorCrt cc = new ColorCrt().setBrightness(50).setContrast(50); 之类的分配——错误是类型不匹配:无法从 CrtScreen 转换为 ColorCrt。第二种方法有效,但写起来很难看。我正在寻找的是一种解决方案,它可以同时解决这两个问题,允许分配子类 方法链接 而无需在任何地方指定类型。也许这需要一种完全不同的方法。
  • CrtScreen 是 ColorCrt 的基础,因此 Color... 可转换(多态兼容)为 CrtScreen,但反之亦然 - 这是故意的。您可以在转换为基础时进行分配,但不能派生。在这两种情况下,它们都与 Screen(所有基础)多态兼容,这正是您想要的。我不确定你想做什么。也许您应该发布更多代码,因为在我看来,我发布的成本已经足够了
  • @Paramaeleon,除此之外,我认为在这种情况下你根本不需要泛型。您的基础应该通过其接口为您提供所需的所有功能,否则您会误解多态性。
  • 您在Screen 的声明中使用T 绑定的原始类型Screen
【解决方案2】:

最后我通过将interface 设置为abstract 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("...");
}

【讨论】:

  • 这是一个损坏的通用结构。只需考虑ColorCrt cc = new CrtScreen().setBrightness(0);,它现在编译时没有任何错误,但显然会在运行时失败。这就是为什么您会收到您正在抑制的这些 unchecked 警告。
  • 为什么不依赖协变返回类型呢?例如,看看StringBuilder 是如何实现Appendable 的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-02-25
  • 1970-01-01
  • 1970-01-01
  • 2018-05-10
  • 2016-01-06
  • 2016-02-20
  • 1970-01-01
相关资源
最近更新 更多