【问题标题】:Why this generic method call is not working?为什么这个通用方法调用不起作用?
【发布时间】:2013-02-26 17:48:48
【问题描述】:

以下代码来自 Effective Java 书:

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)

【问题讨论】:

    标签: java generics effective-java


    【解决方案1】:

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

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

    【讨论】:

    • 你为什么返回 null ?难道不应该返回两个传球的并集吗?
    • 在演示编译问题时,运行时行为并不重要。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • Java 7 也失败了。
      【解决方案4】:

      Java 编译器尝试尽可能地缩小返回类型。在尝试模拟此示例后,我收到以下编译器错误消息,但未指定 .&lt;Number&gt;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);
      

      它试图在“E”中包含Comparable,因为整数和双精度也是Comparable。所以这就是为什么你必须告诉编译器,不,我只想要Number.&lt;Number&gt;union

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

      【讨论】:

        【解决方案5】:

        当你说

        Set numbers = Union.union(integers, doubles) 

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

        static  Set union(Set s1, Set s2) 

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

        Set numbers = GenericDemo.union(integers, doubles)

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

        【讨论】:

          【解决方案6】:

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

          联合(整数,双精度);

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

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

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

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

          【讨论】:

            猜你喜欢
            • 2014-05-26
            • 2015-10-05
            • 2015-07-08
            • 1970-01-01
            • 1970-01-01
            • 2015-07-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多