简体   繁体   English

Java中的Varargs和泛型

[英]Varargs and Generics in java

Consider the following scenario: 请考虑以下情形:

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

And then I call it using a Integer[] . 然后我使用Integer[]调用它。 How does the compiler assume that T is an Integer , and not a Integer[] ? 编译器如何假设TInteger而不是Integer[] (Note, I'm glad that this is the case, but I still find the ambiguity odd). (请注意,我很高兴是这种情况,但是我仍然感到模棱两可)。

Furthermore, if I wanted T to be Integer[] , is there anyway for me to do that (assuming boxing/unboxing doesn't exist)? 此外,如果我希望TInteger[] ,那么我是否有必要这样做(假设装箱/拆箱不存在)?

The Java compiler is smart enough to know that, since you gave it an Integer[] , you probably meant for T to be Integer , not Integer[] . Java编译器很聪明,可以知道,因为您给了它一个Integer[] ,所以您可能打算让TInteger ,而不是Integer[] I'd assume this is part of the Java language specification that defines ... as varargs. 我假设这是Java语言规范的一部分,该规范将...定义为varargs。

If you want to specify what T is, you can do that with the following syntax: 如果要指定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

Generics works on object references, so <T> will work on object references of a class. 泛型适用于对象引用,因此<T>将适用于类的对象引用。 int[] is a class that references an array of int , while int is a primitive. int[]是引用int数组的类,而int是基元。 Integer[] is a class that references to an array of Integer , where Integer is another class. Integer[]是一个引用Integer数组的类,其中Integer是另一个类。

After reviewing this, the varargs param T ... args expects an array of object references, so int[] would be a single element in the array of object references, while Integer[] is an array of object references. 对此进行回顾之后,varargs参数T ... args需要一个对象引用数组,因此int[]将是对象引用数组中的单个元素,而Integer[]是对象引用数组。

If you want to send an Integer[] as each element of your varargs, you can send an Integer[][] . 如果要发送Integer[]作为varargs的每个元素,则可以发送Integer[][] I wrote an example: 我写了一个例子:

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

Output (the hash code of the array will change on every run): 输出(数组的哈希码将在每次运行时更改):

[I@8dc8569

1
2
3

[Ljava.lang.Integer;@45bab50a

There are 3 phases in finding the applicable methods. 寻找适用的方法分为三个阶段。 On the 1st phase, javac tries to match argument types and method parameter types exactly. 在第一阶段,javac尝试完全匹配参数类型和方法参数类型。 The parameter type of the method is T[] on this phase, the argument type is Integer[] , the two matche after T is inferred to be Integer , therefore the method is chosen as the applicable method (there are no other overloading methods to consider). 参数类型的方法的是T[]在此阶段,参数类型为Integer[]后两个matche T被推断为Integer ,因此该方法被选择作为适用方法(没有其他重载方法来考虑)。 No further phases are carried out. 没有进一步的阶段。

If the 1st phase does not yield an applicable method, javac will continue to other phases. 如果第一阶段没有产生适用的方法,则javac将继续其他阶段。 For example, if T is explicitly specified as Integer[] , the method will not match on the 1st phase (because T[] would not match Integer[] ) 例如,如果T被显式指定为Integer[] ,则该方法在第一阶段将不匹配(因为T[]Integer[]不匹配)

On the 3rd phase, varargs are considered; 在第三阶段,考虑使用varargs。 javac will match T , not T[] , with trailing argument types. javac会将T而不是T[]与尾随参数类型进行匹配。

This is indeed, quite confusing, and appear to be ambiguous to our intuition. 确实,这非常令人困惑,并且似乎与我们的直觉相矛盾。

Note that Generics isn't completely relevant to the question. 请注意,泛型与问题并不完全相关。 The exact same question would apply if the function signature were void function(Object... args) -- if you pass an expression of type Integer[] , it could interpreted as either using the array as args , or as one of the elements of args . 如果函数签名为void function(Object... args) ,则将出现完全相同的问题—如果您传递Integer[]类型的表达式,则可以解释为将数组用作args或作为元素之一的args

The answer is that, basically, the compiler will prefer to use the argument as args if possible. 答案是,基本上,如果可能的话,编译器将更喜欢将参数用作args Since the expression you are passing has "array of reference type" type, it is compatible with args , and therefore, that interpretation prevails. 由于您传递的表达式具有“引用类型的数组”类型,因此它与args兼容,因此该解释为准。

Furthermore, if I wanted T to be Integer[], is there anyway for me to do that (assuming boxing/unboxing doesn't exist)? 此外,如果我想让T为Integer [],那么我是否有必要这样做(假设装箱/拆箱不存在)?

Since it is a generic method, you can explicitly specify the the type argument when calling: this.<Integer[]>function(...) . 由于它是通用方法,因此可以在调用时显式指定type参数: this.<Integer[]>function(...)

But back to the more general question where the function signature is void function(Object... args) . 回到更普遍的问题,函数签名是void function(Object... args) You could explicitly create the array of arguments yourself: 您可以自己显式创建参数数组:

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

or (simpler) you can cast the expression to a type that is no longer an array of reference type: 或者(更简单),您可以将表达式转换为不再是引用类型数组的类型:

function((Object)myIntegerArray);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM