Java中的Varargs和泛型

时间:2013-11-15 21:28:12

标签: java generics variadic-functions

考虑以下情况:

<T> void function(T...args){
   ...code...
}

然后我使用Integer[]来调用它。编译器如何假设TInteger而不是Integer[]? (注意,我很高兴这种情况,但我仍然觉得模糊不清)。

此外,如果我希望TInteger[],那么我是否仍然可以这样做(假设不存在装箱/拆箱)?

4 个答案:

答案 0 :(得分:2)

Java编译器非常聪明,因为你给它一个Integer[],你可能认为TInteger,而不是Integer[]。我认为这是Java语言规范的一部分,它将...定义为varargs。

如果要指定T是什么,可以使用以下语法执行此操作:

Integer[] ary = { 1, 2, 3 };
myObj.function(ary); // T is Integer
myObj.<Integer>function(ary); // T is Integer
myObj.<Integer[]>function(ary); // T is Integer[]


<Integer>function(ary); // this is invalid; instead you could do...
this.<Integer>function(ary); // this if it's an instance method
MyClass.<Integer>function(ary); // or this if it's static

答案 1 :(得分:1)

泛型适用于对象引用,因此<T>将适用于类的对象引用。 int[]是一个引用int数组的类,而int是一个基元。 Integer[]是一个引用Integer数组的类,其中Integer是另一个类。

在审查之后,varargs参数T ... args需要一个对象引用数组,因此int[]将是对象引用数组中的单个元素,而Integer[]是一个数组对象引用。

如果您希望发送Integer[]作为varargs的每个元素,则可以发送Integer[][]。我写了一个例子:

public class SomeMain {

    static <T> void foo(T...ts) {
        for(T t : ts) {
            System.out.println(t);
        }
        System.out.println();
    }

    public static void main(String[] args) {
        int[] ints = { 1, 2, 3 };
        Integer[] integers = { 1, 2, 3 };
        foo(ints);
        foo(integers);
        //note, here each element in the varags will behave as Integer[]
        foo(new Integer[][] { integers });
    }
}

输出(每次运行时数组的哈希码都会改变):

[I@8dc8569

1
2
3

[Ljava.lang.Integer;@45bab50a

答案 2 :(得分:1)

找到适用的方法有三个阶段。在第一阶段,javac尝试精确匹配参数类型和方法参数类型。该阶段的方法参数类型为T[],参数类型为Integer[],推断T之后的两个matche为Integer,因此选择方法作为 适用的方法(没有其他重载方法可以考虑)。没有进一步的阶段。

如果第一阶段没有产生适用的方法,javac将继续其他阶段。例如,如果T明确指定为Integer[],则该方法在第一阶段将不匹配(因为T[]Integer[]不匹配)

在第三阶段,考虑varargs; javac将匹配T,而不是T[]与尾随参数类型。

这确实令人困惑,而且似乎对我们的直觉含糊不清。

答案 3 :(得分:0)

请注意,泛型与问题并不完全相关。如果函数签名为void function(Object... args),则完全相同的问题将适用 - 如果传递类型为Integer[]的表达式,则可以将其解释为使用数组作为args,或者作为其中一个args的元素。

答案是,基本上,如果可能,编译器更愿意将参数用作args。由于您传递的表达式具有“引用类型数组”类型,因此它与args兼容,因此,该解释占优势。

  

此外,如果我想要T为Integer [],那么无论如何我都可以   这样做(假设不存在装箱/拆箱)?

由于它是通用方法,因此您可以在调用时this.<Integer[]>function(...)显式指定type参数。

但回到函数签名为void function(Object... args)的更一般的问题。您可以自己显式创建参数数组:

function(new Integer[][]{ myIntegerArray });

或(更简单)您可以将表达式强制转换为不再是引用类型数组的类型:

function((Object)myIntegerArray);