简体   繁体   English

java 是如何处理泛型可变参数的?

[英]How does java deal with generic varargs exactly?

I have a generic class called Foo<T> .我有一个名为Foo<T>的通用类。

public class Foo<T> {
    public void doSomething() {
        Class<T> klass = T.class;  // This won't compile
    }
}

After some googling, I find out that since java implements generics by doing type erasure, it has no idea about the runtime type of T and refuses to compile.经过一番谷歌搜索,我发现由于java通过执行类型擦除来实现泛型,因此它不知道T的运行时类型并且拒绝编译。 However, if I try this...但是,如果我尝试这个......

public class Foo<T> {
    public void doSomething(T... ts) {
        Class<T> klass = (Class<T>) ts.getClass().getComponentType();
        System.out.println(klass);
    }
}

...and run the following code... ...并运行以下代码...

Foo<String> foo1 = new Foo<>();
foo1.doSomething("foo", "bar");  // class java.lang.String
foo1.doSomething();  // class java.lang.String

Foo foo2 = foo1;
foo2.doSomething("foo", "bar");  // class java.lang.Object
foo2.doSomething();  // class java.lang.Object

Foo<? super String> foo3 = foo1;
foo3.doSomething("foo", "bar");  // class java.lang.Object
foo3.doSomething();  // class java.lang.Object

The first one prints class java.lang.String while the other two prints class java.lang.Object , which does not make a lot of sense to me - how can an object's behavior depend on the variable type it's assigned to?第一个打印class java.lang.String而其他两个打印class java.lang.Object ,这对我来说没有多大意义 - 对象的行为如何取决于它分配给的变量类型? Besides, if java does type erasure, how can it know the type parameter is actually java.lang.String in the first case after all?另外,如果java做了类型擦除,它又怎么知道第一种情况下类型参数实际上是java.lang.String呢? And, as I can't assign an Object to T , how can java assign an Object[] to T[] ?而且,由于我无法将Object分配给T ,java 如何将Object[]分配给T[] Seems it's breaking type safety!似乎它破坏了类型安全!

I suppose java passes the runtime class implicitly here, as it can't be inferred from the arguments because there's no argument at all at the second method call.我想 java 在这里隐式地传递了运行时类,因为它不能从参数中推断出来,因为在第二个方法调用中根本没有参数。 I wonder what java is doing under the hood and I'd be glad if someone can tell me more about generic varargs.我想知道 java 在幕后做了什么,如果有人能告诉我更多关于通用可变参数的信息,我会很高兴。

I'm new to StackOverflow.我是 StackOverflow 的新手。 If I break some rules, please let me know:)如果我违反了一些规则,请告诉我:)

Firstly, varargs are just syntactic sugar for an array argument.首先,可变参数只是数组参数的语法糖。 So:所以:

clazz.varArgsMethod("A", "B");

is just the same as (and can be invoked as):与(并且可以作为)相同:

clazz.varArgsMethod(new SomeElementType[]{"A", "B"});

where SomeElementType is determined by the compiler.其中SomeElementType由编译器确定。


At the call site:在呼叫站点:

Foo<String> foo1 = new Foo<>();
foo1.doSomething("foo", "bar");
foo1.doSomething();

the compiler knows that doSomething is a varargs method, and needs a T[] as the varargs argument.编译器知道doSomething是一个可变参数方法,并且需要一个T[]作为可变参数参数。 Because it knows that T is String for a Foo<String> , it knows that T has to be String .因为它知道TString为一个Foo<String> ,它知道T必须是String So it compiles this code to:所以它将这段代码编译为:

Foo<String> foo1 = new Foo<>();
foo1.doSomething(new String[]{"foo", "bar"});
foo1.doSomething(new String[]{});

The String[] array type has a String component type. String[]数组类型具有String组件类型。 The method is passed instances of String[] , which can be queried reflectively for their component type.该方法传递了String[]实例,可以反射性地查询它们的组件类型。


It's basically the same for the other two cases: T is determined from the type of the Foo :其他两种情况基本相同: TFoo的类型确定:

  • For raw Foo , T is erased.对于原始FooT被擦除。 The erasure of T is Object T的擦除是Object
  • For Foo<? super String>对于Foo<? super String> Foo<? super String> , the upper bound of T is Object Foo<? super String> , T的上界为Object

Hence, it creates and passes an Object[] .因此,它创建并传递一个Object[]

...And, as I can't assign an Object to T, how can java assign an Object[] to T[]? ...而且,由于我无法将 Object 分配给 T,java 如何将 Object[] 分配给 T[]? Seems it's breaking type safety!似乎它破坏了类型安全!

Yes, you are correct.是的,你是对的。 Varargs of generic parameter types actually do violate Java's own type safety rules.泛型参数类型的可变参数实际上确实违反了 Java 自己的类型安全规则。

Why?为什么?

Because of the fact that arrays are covariant (and reified) whereas generics are invariant (and erased, or non-reified).因为数组是协变的(和具体化的)而泛型是不变的(被擦除的,或非具体化的)。 In the words of Joshua Bloch, "arrays and generics do not play well together."用 Joshua Bloch 的话来说,“数组和泛型不能很好地结合在一起。” The developers of the Java language made a very deliberate decision to implement the varargs language feature despite the fact that it violates type safety. Java 语言的开发人员做出了一个非常深思熟虑的决定来实现 varargs 语言功能,尽管它违反了类型安全。 They concluded that the value of having varargs (which are extensively used by the reflection and annotation facilities) justified their risks and limitations.他们得出结论,拥有可变参数(被反射和注释工具广泛使用)的价值证明了它们的风险和局限性。

In Java, you cannot compile this code:在 Java 中,您不能编译此代码:

T[] genericArray = new T[SIZE];

This is illegal because instantiating an array of a non-reified type is not type safe according to Java's own type safety rules.这是非法的,因为根据 Java 自己的类型安全规则,实例化非具体类型的数组不是类型安全的。 Yet, when a client invokes a method that has a varargs parameter of a generic type, the runtime system will instantiate an array of that generic type!然而,当客户端调用具有泛型类型的 varargs 参数的方法时,运行时系统将实例化该泛型类型的数组! It's not type safe!它不是类型安全的!

If the varargs system is not used carefully, ClassCastException s or other bugs can occur in the program.如果不小心使用可变参数系统,程序中可能会出现ClassCastException或其他错误。 Essentially, an unenforceable contract is implied between the developer and the runtime system:本质上,开发人员和运行时系统之间隐含了一个不可执行的合同:

The array resulting from the generic varargs parameter...由通用 varargs 参数产生的数组...

  • will only be read from只会被读取
  • will never be written to永远不会被写入
  • will never be returned as a result结果永远不会返回
  • will not be passed to another client (unless that client also promises to only read from the array)不会传递给另一个客户端(除非该客户端也承诺只从数组中读取)

In other words, the developer promises to use the resulting generic array only as a variable number of arguments to complete a computation.换句话说,开发人员承诺将生成的通用数组用作完成计算的可变数量的参数。

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

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