当前位置: 首页 > 知识库问答 >
问题:

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

邵伟泽
2023-03-14

我得到了一个编译错误,我不明白原因。

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

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

下面是一个简单的例子:

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

为什么会出现编译错误?我怎样才能使它按预期工作?

编辑:

由于下面的答案,我通过以下两个更改成功地解决了这个问题

1-我将BuilderChildStatic类generic更改为正常的有界泛型类型,而没有(奇怪的重复出现的泛型模式)内容,

因此,它将如下所示:

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

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

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

除了这两点之外,问题中的其他内容都保持不变

这样它就像我预期的那样。

感谢伟大的答案和解释

共有3个答案

陶弘业
2023-03-14

Philip Voronov的解释是正确的(我赞成),但是既然你也要求解释如何修复它 . . . 最简单的修复方法是将每个构建器类一分为二:

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

例如,如果您将BuilderChildStaticBuilderChildStatic重命名为BuilderChiltGenericBuilderChildGeneric(分别),那么您可以编写:

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

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

卫君博
2023-03-14

您使用原始类型,因此在第一种情况下builder。withChildStuff(“childStuff”)返回BuilderChildStatic(从类型参数边界)的值,该值具有父方法和父ID;在第二种情况下,builder。withParentId(1)返回BuilderParentStatic的值,因此该值没有子方法。

公西马鲁
2023-03-14

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

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

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

 类似资料:
  • 此代码在Java 8中编译,但在Java 7中编译失败: 它不能推断从映射返回的映射的具体类型。空(): 如果将调用更改为f(Map),则会编译。 但是如果您将调用更改为f(Map.empty())。放(1,“A”)。put(2,“B”)) ,它无法在Java 7和8上再次编译。为什么?

  • 我使用的是Java8。 我最近遇到了这个问题: 这不会抛出java.lang.ClassCastException,为什么呢? 我总是想<code>和<code>System.out。println调用。但当我尝试这样做时,它会像预期的那样抛出异常。

  • 编译时,此代码将产生以下+错误: 问题是访问中的类型T与列表中的T不相同。如何修复此编译问题?

  • 问题内容: 没有静态成员可以使用类型参数,但是可以使用泛型类型参数调用静态成员吗​​?例如:- 这里的add()是一个静态方法。 在类似的主题上也有一些C#问题和答案,但是我不太确定如何在Java中使用它。 问题答案: 不,如果A是泛型类型,则无法执行。(Bozho对fast的回答是:),可能认为A是具体类型。 可行的方法如下。 但这可能不是您想要的。 阅读您的评论后,听起来您真正想要做的是: 您

  • 我一直在尝试泛型,很快我就遇到了一些我无法解释的事情 例如: 我不明白

  • 问题内容: 如果在Java中创建泛型类(该类具有泛型类型参数),则可以使用泛型方法(该方法带有泛型类型参数)吗? 考虑以下示例: 正如您对通用方法所期望的那样,我可以使用任何对象调用的实例: 但是,如果我尝试使用 不 指定泛型类型的实例,则无论传入什么,我都会调用返回, 奇怪的是,如果返回类型是通用类,它将编译(例如(实际上,这可以解释-参见下面的答案)): 此外,如果输入通用类,即使仅使用通配符