代码简化如下:
给定两个Java类文件Edge.java和Node.java,每个泛型类都有两个类型参数,N和E.类型参数分别与上限泛型类型Node和Edge绑定。
Node.java:
public class Node<N extends Node<N, E>, E extends Edge<N, E>> {
public void test(E edge) {
// do something...
}
}
Edge.java:
public class Edge<N extends Node<N, E>, E extends Edge<N, E>> {
public void m(N src) {
src.test(this);
}
}
当我编译两个java文件时,发生了错误:
$ javac Node.java Edge.java -Xdiags:verbose
Edge.java:3: error: method test in class Node<N#2,E#2> cannot be applied to given types;
src.test(this);
^
required: E#1
found: Edge<N#1,E#1>
reason: argument mismatch; Edge<N#1,E#1> cannot be converted to E#1
where E#1,N#1,N#2,E#2 are type-variables:
E#1 extends Edge<N#1,E#1> declared in class Edge
N#1 extends Node<N#1,E#1> declared in class Edge
N#2 extends Node<N#2,E#2> declared in class Node
E#2 extends Edge<N#2,E#2> declared in class Node
1 error
似乎方法test
需要一个类型为E#1的参数。但是,它也说E#1是从Edge扩展的......
我已经尝试了各种方法来解决这个问题,但似乎我只能通过修改Edge.java来解决这个问题:
public class Edge<N extends Node, E extends Edge<N, E>> {
public void m(N src) {
src.test(this);
}
}
因此,其中一个上限节点已更改为原始类型。它编译,但它发出警告:
$ javac Node.java Edge.java -Xlint:unchecked
Edge.java:3: warning: [unchecked] unchecked call to test(E) as a member of the raw type Node
src.test(this);
^
where E,N are type-variables:
E extends Edge<N,E> declared in class Node
N extends Node<N,E> declared in class Node
1 warning
它有效,但后来我丢失了泛型类型,警告可能会导致未来出现问题。所以这是我的三个问题:
答案 0 :(得分:0)
为什么原始Edge.java无法正常工作并输出此错误?
因为无法保证Edge
已使用自己的类型进行参数化。下面的子类将被编译而没有错误,同时违反了this
是E
的假设:
class EdgeImpl1 extends Edge<NodeImpl, EdgeImpl2> {}
更一般地说,Java不支持通用自我类型的概念。
为什么第二个Edge.java会编译?
因为原始类型具有传染性。原始test()
类型的Node
方法具有原始Edge
参数,因此它将接受任何Edge
实例。
解决此问题的最佳方法是什么?
不幸的是,没有完美的解决方案。一种方法是简单地将this
投射到E
:
src.test((E)this);
如果自我类型被滥用,这可能会导致ClassCastException
,如上例所示。一个更安全但更冗长的替代方法是声明一个抽象方法,为每个子类返回适当的E
:
abstract class Edge<N extends Node<N, E>, E extends Edge<N, E>> {
public void m(N src) {
src.test(returnThis());
}
protected abstract E returnThis();
}
class EdgeImpl extends Edge<NodeImpl, EdgeImpl> {
@Override
protected EdgeImpl returnThis() {
return this;
}
}