1

以下代码来自 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)
4

6 回答 6

5

请注意,Double 和 Integer 不仅扩展了 Number,还实现了 Comparable。因此编译器猜测的返回类型将是 Set<Number&Comparable>,它不能转换为 Set<Number>。您需要告诉编译器要使用哪种跟随类型。使用后续代码,您不需要确切的类型。

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;

}
于 2013-02-26T18:05:10.927 回答
1

唯一的问题是编译器不够聪明,无法确定要替换的类型E,因此您必须明确指定它。该习语没有名称,它只是您显式指定泛型类型参数的方式。

于 2013-02-26T17:55:10.690 回答
1

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

我也不知道这个“成语”的名称,它只是称为通用函数。

于 2013-02-26T17:59:42.147 回答
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

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

于 2013-02-26T18:02:48.540 回答
0

当你说

Set numbers = Union.union(integers, doubles) 

这里 Union 是包含方法静态方法 union 的类的名称

static  Set union(Set s1, Set s2) 

因此,如果您的类名是定义了方法 union 的 GenericDemo,那么您将编写的代码-

Set numbers = GenericDemo.union(integers, doubles)

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

于 2014-02-06T16:54:16.977 回答
0

它都是关于泛型符号原则和概念的。我正在从 ijrandom 发布的示例中前进。让我们以 U 和 V 仅实现 X 的第一个场景为例。目前我们没有将方法输出分配给参考变量。我们调用了联合方法,如下所示:

联合(整数,双打);

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

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

union(integers, doubles);//注意我们还没有将返回值赋给变量。

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

于 2014-04-29T12:18:04.823 回答