使用有界类型变量

时间:2018-06-04 20:08:34

标签: java class generics types

代码简化如下:

给定两个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

它有效,但后来我丢失了泛型类型,警告可能会导致未来出现问题。所以这是我的三个问题:

  1. 为什么原始Edge.java无法正常工作并输出此错误?
  2. 为什么第二个Edge.java会编译?
  3. 解决此问题的最佳方法是什么?

1 个答案:

答案 0 :(得分:0)

  

为什么原始Edge.java无法正常工作并输出此错误?

因为无法保证Edge已使用自己的类型进行参数化。下面的子类将被编译而没有错误,同时违反了thisE的假设:

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