在Java中使用泛型动态返回调用类的类型

在Java中使用泛型动态返回调用类的类型,java,generics,this,Java,Generics,This,考虑以下几类 public class ClassA { public ArrayList<String> v1 = new ArrayList<>(); public ClassA addOnce(String s1) { v1.add(s1); return this; } } public class ClassB extends ClassA { public ClassB addTwic

考虑以下几类

public class ClassA {

    public ArrayList<String> v1 = new ArrayList<>();

    public ClassA addOnce(String s1) {
        v1.add(s1);
        return this;
    }

}


public class ClassB extends ClassA {

    public ClassB addTwice(String s2) {
        v1.add(s2);
        v1.add(s2);
        return this;
    }

}

但编译器仍然要求强制转换,因为它无法仅根据下一个方法调用找到返回类型,因为该方法可能可用于多个类addOnce可用于ClassA和ClassB,并且返回类型不会收敛。是否有一种方法可以将其与调用类的类型一起返回,通常是在子类调用父类方法时?

要使类型推断工作,您需要在ClassA中声明泛型参数。ClassA应如下所示:

public class ClassA<T extends ClassA<T>> {
  public ArrayList<String> v1 = new ArrayList<>();

  public T addOnce(String s1) {
    v1.add(s1);
    return (T) this;
  }
}

阅读更多关于

有两种方法可以做到这一点,但我不推荐任何一种

方法一:泛型 具体来说,递归类指回泛型类本身:

public class ClassA<R extends ClassA<R>> { // note the recursive nature
    public ArrayList<String> v1 = new ArrayList<>();

    @SuppressWarnings("unchecked")
    protected R self() {
      return (R) this;
    }

    public R addOnce(String s1) {
        v1.add(s1);
        return self();
    }
}
这意味着您的大多数用例将使用通配符的AbstractClassA类型。除此之外,如果您尝试序列化这些实例,则会出现问题。类返回一个类

此外,AbstractClassA的子类不继承该类型,因此,例如,如果您想定义将模式保留在ClassB中的新方法,则必须在每个方法中使用新的泛型参数重复该模式。我的预测是你会后悔的

方法二:覆盖每个方法 另一种方法是手动重写基类中的所有方法。当您重写一个方法时,您可以。因此:

这种方法很好用,但有点笨拙。大多数情况下,人们会首先在基类上设置内容,然后在子类上设置特定于子类的内容。如果你愿意扭转这种局面,并且你真的想要这些链式方法,这可能是我会选择的方法。但我真正的建议是:

我的建议是:算了吧
Java不能很好地处理这些类型的方法链。我建议你把整个事情都忘了,不要在这一点上与语言对抗。使方法返回void,并将每个调用放在自己的行上。在使用了尝试了第一种和第三种方法的代码之后,我发现它周围的丑陋很快就会变老,而且通常是一种不值的痛苦。我从未见过第二种方法有效,因为这是一种非常痛苦的方法,以至于没有人愿意尝试

这个问题让我想起了方法重写


为什么A类甚至应该知道B类的存在。这是B的责任。在这个解决方案中,类B将向其超类方法添加一次的功能委托给它,但是类型转换是由B在返回值之后执行的。在当前用例中确实不需要使用泛型。

我得到了覆盖方法,但我仍然不理解您的方法1。尽管如此,新的ClassB.addOnce1.addTwice2无法与方法1一起工作。我错过什么了吗?顺便说一句,我也不会写/建议写这样的代码,但只是想知道实现这一点的可能性。哦,对不起,我有一个打字错误。addOnce应该返回R,而不是ClassA。我会修正它的。是的,我猜错了,并且修正了返回类型为R的错误。在我的机器上运行,虽然我必须添加v1,但我忘了从你的示例中复制它。不幸的是,我今晚要出去,所以我暂时不能再做进一步的编辑了。祝你好运方法1有效!我忘了做ClassB扩展ClassA,感谢您没有在您的答案中只添加这一点,现在我已经探索并理解了它是如何工作的:
public class ClassA<R extends ClassA<R>> { // note the recursive nature
    public ArrayList<String> v1 = new ArrayList<>();

    @SuppressWarnings("unchecked")
    protected R self() {
      return (R) this;
    }

    public R addOnce(String s1) {
        v1.add(s1);
        return self();
    }
}
// AbstractClassA looks like ClassA above; it contains addOnce
class ClassA extends AbstractClassA<ClassA> {
    @Override
    public ClassA self() {
        return this;
    }
}

class ClassB extends AbstractClassA<ClassB> {
    ClassB addTwice(String s2) {
        ...
        return this;
    }
}
public class ClassA {

    public ArrayList<String> v1 = new ArrayList<>();

    public ClassA addOnce(String s1) {
        v1.add(s1);
        return this;
    }

}
public class ClassB extends ClassA {

    public ClassB addOnce(String s2) {
        super.addOnce(s2);
        return this; // same reference as in the super method, but now typed to ClassB
    }
    ...

}
new ClassB().addTwice("2").addOnce("1");
class A {

    protected List<String> list = new LinkedList<>();

    public A addOnce(String s) {
        list.add(s);
        return this;
    }
}

class B extends A {

    public B addOnce(String s) {
        return (B) super.addOnce(s);
    }

    public B addTwice(String s) {
        super.addOnce(s);
        super.addOnce(s);
        return this;
    }
}