[英]Overriding Java generic methods
我想创建一个接口,用于将 object 复制到同一 class 的目标 object。 简单的方法是使用强制转换:
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);
}
}
但我也找到了一种可以使用 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);
}
}
虽然我发现摆脱警告的唯一方法是注释。 而且感觉好像出了点问题。 那么有什么问题呢? 我可以接受问题的根源有问题。 因此,欢迎任何形式的澄清。
您的接口定义:
public interface Copyable<T extends Copyable<T>> {
void copy(T copyFrom);
}
您的实施:
public class Example implements Copyable<Example> {
private Object data;
void copy(Example copyFrom) {
data = copyFrom.data;
}
//nontrivial stuff
}
那应该照顾你的警告。
假设您不想进一步子类化,您只需要:
public static /*final*/ class AClass implements Copyable<AClass> {
对于抽象的 class,您可以执行“枚举”操作:
public static abstract class AClass<T extends AClass<T>> implements Copyable<T> {
在 testCopy 中,警告之一是因为您正在实例化 Copyable 的“原始类型”,而不是一些具体的 Copyable<T>。 实例化 Copyable 后,它只能应用于 Ts(包括 T 的子类型)。 为了使用正式类型进行实例化,需要稍微更改 class 定义:
public static class A<T extends A> implements Copyable<T>
public static class B<T extends B> extends A<T>
下一个问题是 Copyable<B> 只能传递 B 的编译时类型(基于 Copyable 的定义)。 上面的 testCopy() 传递了一个可复制的编译时类型。 以下是一些可行的示例,并附有简要说明:
public void testExamples()
{
// implementation of A that applies to A and subtypes
Copyable<A> aCopier = new A<A>();
// implementation of B that applies to B and subtypes
Copyable<B> bCopier = new B<B>();
// implementation of A that applies to B and subtypes
Copyable<B> bCopier2 = new A<B>();
}
我已经学习了 Scala,现在我知道我两年前想要的东西可以通过逆变类型参数和 Scala 的类型系统来实现:
trait CopyableTo[-T] {
def copyTo(t: T)
}
class A(private var a: Int) extends CopyableTo[A] {
override def copyTo(t: A) {
println("A:copy")
t.a = this.a
}
}
class B(private var a: Int, private var b: Int) extends A(a) with CopyableTo[B] {
def copyTo(t: B) {
println("B:copy")
super.copyTo(t)
t.b = this.b
}
}
@Test
def zzz {
val b1 = new B(1, 2)
val a1 = new A(3)
val b2 = new B(4, 5)
b1.copyTo(a1)
a1.copyTo(b1)
b1.copyTo(b2)
}
Java 类型的系统太弱了。
我一直在尝试找出一种方法来消除您第一种方法中的警告,但我想不出任何可行的方法。 即便如此,我认为第一种方法是两害相权取其轻。 一个不安全的演员比需要给你的类提供如此复杂的 api 更好。
一种完全独立的方法是覆盖 Object.clone() 并实现 Cloneable。
这是第二种方法的最佳代码。 它编译时没有任何警告。
import static org.junit.Assert.fail;
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<T>> implements Copyable<T> {
private String a = "--A--";
public void copy(T t) {
System.out.println(t.a);
}
@SuppressWarnings("unchecked")
public static Copyable<Object> getInstance() {
return new A();
}
}
public static class B<T extends B<T>> extends A<T> {
private String b = "--B--";
public void copy(T t) {
super.copy(t);
System.out.println(t.b);
}
@SuppressWarnings("unchecked")
public static Copyable<Object> getInstance() {
return new B();
}
}
@Test
public void testCopy() {
Copyable<Object> b1 = B.getInstance();
Copyable<Object> b2 = B.getInstance();
Copyable<Object> a = A.getInstance();
b1.copy(b2); // this works as intended
try {
b1.copy(a); // this throws ClassCastException
fail();
} catch (ClassCastException cce) {
}
}
}
而且我还通过反思弄清楚了这个程序中发生的所有事情:
for (Method method : A.class.getMethods()) {
if (method.getName().equals("copy")) {
System.out.println(method.toString());
}
}
for (Method method : B.class.getMethods()) {
if (method.getName().equals("copy")) {
System.out.println(method.toString());
}
}
这是 output:
public void com.sbp.core.TestGenerics$A.copy(com.sbp.core.TestGenerics$A)
public void com.sbp.core.TestGenerics$A.copy(java.lang.Object)
public void com.sbp.core.TestGenerics$B.copy(com.sbp.core.TestGenerics$B)
public void com.sbp.core.TestGenerics$B.copy(com.sbp.core.TestGenerics$A)
public void com.sbp.core.TestGenerics$A.copy(java.lang.Object)
代表着:
A 和 B 中的 copy(...) 方法使编译器生成“桥” - 每种方法有 2 种不同的方法,一种具有来自祖先的 reifed 参数类型(来自 Copyable 的具体 T 变为 Object,来自 A 的具体“T extends A”变为 A ) 这就是为什么它是覆盖而不是重载的原因,而另一个具有用于定义 class 的具体参数类型的原因。 第一种方法(带有自动生成的主体)向下转换其参数以调用第二种方法(他们称其为桥)。 由于这种向下转换,如果我们调用 b1.copy(a),我们会在运行时得到 ClassCastException。
看起来直接类型转换对于我的问题来说是更清洁和更好的工具,并且 generics 更好地用于它们的直接目的 - 强制编译时类型检查。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.