重写 Java 泛型方法
我想创建一个接口,用于将对象复制到同一类的目标对象。 简单的方法是使用强制转换:
import org.junit.Test;
import org.junit.internal.runners.JUnit4ClassRunner;
import org.junit.runner.RunWith;
@RunWith(JUnit4ClassRunner.class)
public class TestGenerics {
public static interface Copyable {
public void copy(Copyable c);
}
public static class A implements Copyable {
private String aField = "--A--";
protected void innerCopy(Copyable c) {
A a = (A)c;
System.out.println(a.aField);
}
public void copy(Copyable c) {
innerCopy(c);
}
}
public static class B extends A {
private String bField = "--B--";
protected void innerCopy(Copyable c) {
B b = (B)c;
super.innerCopy(b);
System.out.println(b.bField);
}
}
@Test
public void testCopy() {
Copyable b1 = new B();
Copyable b2 = new B();
b1.copy(b2);
}
}
但我也找到了一种可以使用泛型来完成的方法:
import org.junit.Test;
import org.junit.internal.runners.JUnit4ClassRunner;
import org.junit.runner.RunWith;
@RunWith(JUnit4ClassRunner.class)
public class TestGenerics {
public static interface Copyable<T> {
public void copy(T t);
}
public static class A<T extends A<?>> implements Copyable<T> {
private String a = "--A--";
public void copy(T t) {
System.out.println(t.a);
}
}
public static class B<T extends B<?>> extends A<T> {
private String b = "--B--";
public void copy(T t) {
super.copy(t);
System.out.println(t.b);
}
}
@Test
@SuppressWarnings("unchecked")
public void testCopy() {
Copyable b1 = new B();
Copyable b2 = new B();
b1.copy(b2);
}
}
虽然我发现摆脱警告的唯一方法是注释。 而且感觉好像有什么不对劲。 那么出了什么问题呢? 我可以接受问题的根源是有问题的。 因此,欢迎任何形式的澄清。
I wanted to create an interface for copying an object to a destination object of the same class. The simple way is to use casting:
import org.junit.Test;
import org.junit.internal.runners.JUnit4ClassRunner;
import org.junit.runner.RunWith;
@RunWith(JUnit4ClassRunner.class)
public class TestGenerics {
public static interface Copyable {
public void copy(Copyable c);
}
public static class A implements Copyable {
private String aField = "--A--";
protected void innerCopy(Copyable c) {
A a = (A)c;
System.out.println(a.aField);
}
public void copy(Copyable c) {
innerCopy(c);
}
}
public static class B extends A {
private String bField = "--B--";
protected void innerCopy(Copyable c) {
B b = (B)c;
super.innerCopy(b);
System.out.println(b.bField);
}
}
@Test
public void testCopy() {
Copyable b1 = new B();
Copyable b2 = new B();
b1.copy(b2);
}
}
But also i've found a way it can be done using generics:
import org.junit.Test;
import org.junit.internal.runners.JUnit4ClassRunner;
import org.junit.runner.RunWith;
@RunWith(JUnit4ClassRunner.class)
public class TestGenerics {
public static interface Copyable<T> {
public void copy(T t);
}
public static class A<T extends A<?>> implements Copyable<T> {
private String a = "--A--";
public void copy(T t) {
System.out.println(t.a);
}
}
public static class B<T extends B<?>> extends A<T> {
private String b = "--B--";
public void copy(T t) {
super.copy(t);
System.out.println(t.b);
}
}
@Test
@SuppressWarnings("unchecked")
public void testCopy() {
Copyable b1 = new B();
Copyable b2 = new B();
b1.copy(b2);
}
}
Though the only way i've found to get rid of warnings is the annotation. And it feels like something is wrong.
So what's wrong? I can accept that something is wrong in the root of the problem. So any sort of clarification is welcome.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我已经学会了 Scala,现在我知道我两年前想要的东西可以通过逆变类型参数来实现,而 Scala 的类型系统:
Java 类型系统对此太弱了。
I've learned Scala and now i know that the thing i wanted 2 years ago could have been achieved with contravariant type parameter and Scala's type system:
Java type system is too weak for this.
这是第二种方法的最佳代码。 它编译时没有任何警告。
而且我在反射的帮助下弄清楚了这个程序中发生的所有事情:
这是输出:
这意味着:
A和B中的copy(...)方法使编译器生成“桥梁” -
每种方法有 2 种不同的方法,其中一种方法的参数类型来自
祖先(具体化 T 从 Copyable 变为 Object,具体化“T 扩展
A”从 A 变成 A),这就是为什么它是覆盖而不是过载,
另一个具有用于定义类的具体化参数类型。 第一的
方法(带有自动生成的主体)向下转型其参数以调用
第二(他们称之为桥梁)。 由于这种沮丧,我们得到
如果我们调用 b1.copy(a),则会在运行时出现 ClassCastException。
对于我来说,直接类型转换似乎更干净、更好的工具
问题和泛型最好用于其直接目的 -
强制编译时类型检查。
This is the best possible code of second approach. It compiles without any warnings.
And also i figured out all that happens in this program with help of reflection:
Here is the output:
It means that:
The copy(...) methods in A and B make compiler generate "bridges" -
2 different methods for each, one with reifed argument type from
ancestor (reified T from Copyable becomes Object, reified "T extends
A" from A becomes A) and that is why it's override and not overload,
and the other one with reified argument type for defining class. First
method (with autogenerated body) downcasts its argument to call the
second (they call it a bridge). Because of this downcasting we get
ClassCastException in runtime if we call b1.copy(a).
It looks like direct type casting is cleaner and better tool for my
problem and generics are better used in their direct purpose - to
enforce compile time type checking.
我一直在试图找出一种方法来消除第一种方法中的警告,但我想不出任何有效的方法。 即便如此,我认为第一种方法是两害相权取其轻。 不安全的强制转换比需要为类提供如此复杂的 api 更好。
一种完全独立的方法是重写 Object.clone() 并实现 Cloneable。
I keep trying to figure out a way to get rid of the warnings in your first approach and I can't come up with anything that works. Even so, I think the first approach is the lesser of two evils. An unsafe cast is better than needing to give your classes such a complicated api.
A completely separate approach would be to override Object.clone() and implement Cloneable.
在 testCopy 中,警告之一是因为您正在实例化 Copyable 的“原始类型”,而不是某些具体的 Copyable。 一旦实例化 Copyable,它就只能应用于 T(包括 T 的子类型)。 为了用正式类型实例化,需要稍微更改类定义:
下一个问题是 Copyable 。 只能传递 B 的编译时类型(基于 Copyable 的定义)。 上面的 testCopy() 向它传递了一个 Copyable 的编译时类型。 以下是一些可行的示例,并附有简要说明:
In testCopy, one of the warnings is because you're instantiating a "raw type" of Copyable rather than some concrete Copyable<T>. Once you instantiate a Copyable, it can only be applied to Ts (which include subtypes of T). In order to instantiate with a formal type, the class definitions will need to be changed slightly:
The next issue is that a Copyable<B> can only be passed a compile-time type of B (based on the definition of Copyable). And testCopy() above is passing it a compile-time type of Copyable. Below are some examples of what will work, with brief descriptions:
假设您不想进一步子类化,您只需要:
对于抽象类,您可以执行“枚举”操作:
Assuming you don't want to subclass further you just need:
For an abstract class, you do the "enum" thing:
您的接口定义:
您的实现:
这应该处理您的警告。
Your interface definition:
Your implementation:
That should take care of your warnings.