【问题标题】:How is the component type for the varargs array determined?可变参数数组的组件类型是如何确定的?
【发布时间】:2016-07-08 14:01:01
【问题描述】:

当你使用可变参数时,结果数组的组件类型是如何确定的?

例如,这个程序是否保证打印true 或者它的行为在技术上是未指定的?

public static void main(String[] args) {
    foo("", 0);
}

static <T> void foo(T... arr) {
    System.out.println(arr.getClass() == Serializable[].class);
} 

【问题讨论】:

  • 我希望这里的答案是“所有类型推断规则都具有难以理解的复杂性,与我尝试过的每个编译器都至少存在一些不匹配。”

标签: java generics variadic-functions


【解决方案1】:

我运行了这段代码,输出告诉你不能保证(至少如果类在不同的层次结构分支上有多个共同祖先)

老实说,我不知道这种魔法背后的原因,但我无法将其作为评论发布

import java.util.*;
import java.lang.*;
import java.io.*;

class Ideone
{

  interface A{}
  interface B{}
  class AB implements A, B {}
  class BA implements A, B {}

    public static void main (String[] args) throws java.lang.Exception
    {
        foo(new AB(), new BA());
        foo2(new AB(), new BA());
    }

    static <T> void foo(T... arr) {
        System.out.println(arr.getClass() == A[].class);
    }

    static <T> void foo2(T... arr) {
        System.out.println(arr.getClass() == B[].class);
    } 
}

输出

true
false

更奇怪的事情:

如果interface Binterface A之前声明,结果相反:

false
true

implements 块中更改方法调用中的参数顺序、方法声明顺序和接口顺序对我不起作用 (1.8.0_51)。

【讨论】:

    【解决方案2】:

    好吧,我对此不是 100% 确定,但我认为这与可变参数无关,而更像是动态绑定、泛型和类型擦除。

    “Java 中的数组是协变的,但泛型不是。换句话说,String[] 是 Object[] 的子类型,但 Stack 不是 Stack 的子类型。”

    来源: http://algs4.cs.princeton.edu/13stacks/

    因此,为了回答您的问题,此行为已记录在案并符合预期。

    在书中:'Effective Java' Joshua Bloch 是这样解释的:

    数组在两个重要方面不同于泛型类型。首先,数组是协变的。这个听起来很吓人的词的意思很简单,如果 Sub 是 Super 的子类型,那么数组类型 Sub[] 就是 Super[] 的子类型。相比之下,泛型是不变的:对于任何两个不同的类型 Type1 和 Type2,List 既不是 List 的子类型也不是超类型 [JLS, 4.10; Naftalin07, 2.5]。你可能会认为这意味着泛型有缺陷,但可以说是数组有缺陷。

    我为什么要谈论你可能会问的数组?好吧,因为可变参数最终会变成数组。

    “在过去的版本中,采用任意数量值的方法要求您创建一个数组并将值放入数组中,然后再调用该方法。”

    “在一个数组中必须传递多个参数仍然是正确的,但可变参数功能会自动执行并隐藏该过程。”

    来源: https://docs.oracle.com/javase/8/docs/technotes/guides/language/varargs.html

    【讨论】:

      【解决方案3】:

      此答案可能不是您正在寻找的 100% 的答案,但它可能会有所帮助。一般来说, arr.getClass() 会返回一些数组类(最常见的Object[].class,但也可以是Integer[].classNumber[].class,甚至Serializable[].class——通常是所有元素中最具体的类型,但我不会不要指望它,请参阅 m-szalik 答案)。如果要确保数组中包含的所有类都是 Serializable 的实例,则必须检查每个元素(顺便说一句。提供的实现不支持 null 值):

      static <T> void foo(T... arr) {
          System.out.println(Stream.of(arr)
                  .filter(e -> !Serializable.class.isInstance(e.getClass()))
                  .findFirst()
                  .orElse(null) == null);
      }
      

      你可能想看看:

      顺便说一句。我同意Fogetti船长的意见:

      好吧,我对此不是 100% 确定,但我认为这与可变参数无关,而更像是动态绑定、泛型和类型擦除。

      注意

      只是您的foo 实现的一些示例:

      • foo(1, 2) 将是 Integer[].class
      • foo(1, 2.0) 将是 Number[].class
      • foo ("", 1) 将是 Serializable[].class
      • foo(null, 2) 将是 Integer[].class
      • foo("", new Object()) 将是 Object[].class

      【讨论】:

        【解决方案4】:

        这不是关于可变参数,而是更多关于泛型。
        通用信息在编译过程中丢失。您的 varargs 参数由编译器转换为数组。所以 JVM 不决定类型,但编译器决定。

        您的代码被编译为字节码,如下所示:

        public static void main(String[] args) {
            foo(new Serializable[]{"", Integer.valueOf(0)});
        }
        

        决策算法很长,但你可以在这里阅读。 https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html

        【讨论】:

          猜你喜欢
          • 2012-11-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-04-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多