为什么这个泛型方法调用不起作用?

时间:2013-02-26 17:48:49

标签: java generics effective-java

以下代码来自Effective Java book:

Set<Integer> integers = ... ;
Set<Double> doubles = ... ;
Set<Number> numbers = union(integers, doubles);

此代码未编译,作者建议通过告诉编译器确切类型如下来解决此问题:

Set<Number> numbers = Union.<Number>union(integers, doubles)

如果联合的签名如下,为什么早期的程序不能编译?这个特定的解决方法成语是什么?

public static <E> Set<E> union(Set<? extends E> s1,
Set<? extends E> s2)

6 个答案:

答案 0 :(得分:5)

请注意,Double和Integer不仅扩展了Number,还实现了Comparable。因此编译器猜测的返回类型将是Set&lt; Number&amp; Comparable&gt;无法转换为Set&lt; Number&gt;。您需要告诉编译器使用哪种类型。使用以下代码,您不需要确切的类型。

interface X {}
class U implements X {}
class V implements X {}

public static void main(String[] args) {
    Set<U> integers = new HashSet<U>();
    Set<V> doubles = new HashSet<V>();
    Set<X> numbers = union(integers, doubles);
}
public static <E> Set<E> union(Set<? extends E> s1,   Set<? extends E> s2) {
    return null;

}

但是如果你稍微改变它,你会得到原点错误。

   interface X {}
interface Y {}
class U implements X, Y {}
class V implements X, Y {}

public static void main(String[] args) {
    Set<U> integers = new HashSet<U>();
    Set<V> doubles = new HashSet<V>();
    Set<X> numbers = union(integers, doubles);
}
public static <E> Set<E> union(Set<? extends E> s1,   Set<? extends E> s2) {
    return null;

}

答案 1 :(得分:1)

唯一的问题是编译器不够聪明,无法确定用E替换的类型,因此必须明确指定它。这个成语没有名称,只是你如何明确指定泛型类型参数。

答案 2 :(得分:1)

如果union方法定义为<E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2),则较旧的Java编译器不够智能,无法正确推断返回的类型。因此,有必要告诉编译器您希望从该方法返回哪种类型,以确保类型安全。 (我认为Java 7编译器可能能够正确地推断出这一点,但我不确定)。

我也不知道这个'成语'的名称,它只是被称为泛型函数。

答案 3 :(得分:1)

Java编译器尝试尽可能地缩小返回类型。在尝试模拟此示例后,我收到以下编译器错误消息而未指定.<Number>union

EffectiveJava.java:19: incompatible types
found   : java.util.Set<java.lang.Number&java.lang.Comparable<? extends java.lang.Number&java.lang.Comparable<?>>>
required: java.util.Set<java.lang.Number>
      Set<Number> numbers = union(integers, doubles);

它尝试将Comparable包含在“E”中,因为整数和双打都是Comparable。所以这就是为什么你必须告诉编译器,不,我只想Number.<Number>union

据我所知,我不知道这个成语是否有名字。

答案 4 :(得分:0)

当你说

Set numbers = Union.union(integers, doubles) 

此处Union是包含静态方法union

方法的类的名称
static  Set union(Set s1, Set s2) 

所以,如果你的类名是GenericDemo,其中定义了方法union,那么你将编写代码 -

Set numbers = GenericDemo.union(integers, doubles)

如果union是非静态方法,则可以用对象实例替换GenericDemo。

答案 5 :(得分:0)

所有关于泛型符号的原则和概念。我是从ijrandom发布的例子开始的。让我们来看第一个场景,其中U和V仅实现X.目前我们没有将方法输出分配给引用变量。我们调用了union方法,如下所示:

union(整数,双打);

我们知道对于泛型,实际的参数类型是由你在调用方法时指定的类型决定的,所以在这种情况下,方法的返回类型将是Set of X,只需将光标移到方法调用上你就会看到java如何编译器在编译时确定了实际的参数类型。

在我们的第二个场景中,我们再次调用方法union:

union(整数,双精度); //注意我们还没有将返回值赋给变量。

将光标移到方法调用上,您会注意到方法的返回类型更改为Set of unknown extends X,因此编译器无法完全解析实际类型,因为U和V同时实现X和Y,X是因此,只要为变量赋值,就会产生错误,因为编译器仍然无法识别实际类型。在这种情况下,您需要告诉编译器使用哪种实际类型。