在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;
}
}