使用泛型类型的方法调用链接(奇怪的重复通用模式)

时间:2016-10-14 16:44:51

标签: java generics

我收到编译错误,我不明白其原因。

我正在尝试创建一个扩展另一个构建器的构建器,所有构建器都使用泛型类型。

问题是某些泛型方法的返回类型是父类,而不是子类,这会阻止我链接任何子方法。

这是一个简单的例子:

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<>();
除了那两点之外,问题中的其他所有内容都保持不变

这种方式表现得像我预期的那样。

感谢您的答案和解释

3 个答案:

答案 0 :(得分:2)

您使用原始类型,因此在第一种情况下builder.withChildStuff("childStuff")返回BuilderChildStatic的值(来自类型参数边界),并且此值具有父级&#39;方法withParentId;在第二种情况下builder.withParentId(1)返回BuilderParentStatic的值,因此该值不具有子方法。

答案 1 :(得分:2)

Philip Voronov的解释是正确的(我已经对它进行了投票),但是因为你还要求解释如何修复它。 。 。最简单的解决方法是将每个构建器类拆分为两个:

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

例如,如果您将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,就像您现在正在做的那样。

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

答案 2 :(得分:1)

这个答案适用于原始问题,而不是没有重复通用范围的更新问题。

使用原始类型会导致问题。你可以修复&#34;它通过添加通用通配符约束,即:

 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类型。