繁体   English   中英

具有泛型类型的方法调用链接(好奇地重复泛型模式)

[英]Method call chaining with generic types (Curiously Recurring Generic Pattern)

我收到一个我不明白其原因的编译错误。

我正在尝试创建一个扩展了另一个构建器的构建器,全部使用泛型类型。

问题在于某些通用方法的返回类型是父类,而不是子类,这将阻止我链接任何子方法。

这是简单的示例:

public class BuilderParent {
    public static class BuilderParentStatic<B extends BuilderParentStatic<B>> {
        public BuilderParentStatic() {}
        public B withParentId(int rank) { return self(); }
        protected B self() { return (B)this; }
    }
}

public class BuilderChild extends BuilderParent {
    public static class BuilderChildStatic<B extends BuilderChildStatic<B>>
            extends BuilderParent.BuilderParentStatic<B> {
        public BuilderChildStatic() {}
        public B withChildStuff(String s) { return (B)this.self(); }
        protected B self() { return (B)this; }
    }
}

public class Test {
    public static void main(String args[]) {
        BuilderChild.BuilderChildStatic builder = new BuilderChild.BuilderChildStatic();

        // OK (uses child first, then parent):
        builder.withChildStuff("childStuff").withParentId(1);

        // compile error (uses parent first, then child):
        builder.withParentId(1).withChildStuff("childStuff");
    }
}

为什么会出现编译错误? 如何使它按预期工作?


编辑:

我通过使用以下2项更改,通过以下答案设法解决了该问题

1-我将BuilderChildStatic类的泛型更改为没有(好奇重复生成的泛型模式)的普通有界泛型类型,

因此将如下所示:

    public static class BuilderChildStatic<B extends BuilderChildStatic> extends BuilderParent.BuilderParentStatic<BuilderChildStatic<B>> {

2-另一个变化是我避免在main方法的声明中使用原始类型,因为现在我可以在声明时指定类型

        BuilderChild.BuilderChildStatic<BuilderChild.BuilderChildStatic> builder = new BuilderChild.BuilderChildStatic<>();

问题中除这2点之外的所有其他内容都保持不变

这样,它的表现就如我预期的那样。

感谢您的出色回答和解释

您使用原始类型,因此在第一种情况下, builder.withChildStuff("childStuff")返回BuilderChildStatic值(从类型参数范围开始),并且该值具有父方法withParentId 在第二种情况下, builder.withParentId(1)返回BuilderParentStatic值,因此该值没有子方法。

菲利普·沃罗诺夫(Philip Voronov)的解释是正确的(我对此表示反对),但是由于您还要求提供有关如何解决它的解释。 最简单的解决方法是将每个构建器类分为两部分:

  • 一个通用的父级,其定义完全类似于您定义BuilderParentStaticBuilderChildStatic (但可能已重命名)的方式,以实现链接/继承/等。
  • 一个非通用子类,通过指定其父类的type参数可以确保客户端不必这样做。

例如,如果将BuilderParentStaticBuilderChildStatic分别重命名为BuilderParentGenericBuilderChildGeneric ,则可以编写:

public static final class BuilderParentStatic
    extends BuilderParentGeneric<BuilderParentStatic> {
    // empty class definition -- everything we need is in BuilderParentGeneric
}

public static final class BuilderChilderStatic
    extends BuilderChildGeneric<BuilderChilderStatic> {
    // empty class definition -- everything we need is in BuilderChildGeneric
}

然后像现在一样声明并初始化builder

这样,您可以避免使用原始类型(以及它们带来的所有问题),而无需在任何地方指定类型参数。

此答案适用于原始问题,不适用于没有重复通用界限的更新问题。

使用原始类型会导致问题。 您可以通过添加通用通配符约束来“修复”它,即:

 BuilderChild.BuilderChildStatic<?> builder = new BuilderChild.BuilderChildStatic();
 builder.withChildStuff("childStuff").withParentId(1); //works since we used child first then parent
 builder.withParentId(1).withChildStuff("childStuff"); //now works

这样,您返回的值将为BuilderChildStatic类型。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM