类型参数是否与参数一起传递给方法?

Are type arguments passed along with an argument to a method?

提问人:Matthew S. 提问时间:3/4/2021 最后编辑:Matthew S. 更新时间:3/5/2021 访问量:116

问:

从我自己的实验中,我得出结论,当作为参数传递给方法时,对象或对象引用的任何类型参数都会被剥离。如果使用类型参数参数化方法的参数,则会弹出类型参数的问题:

<T> void method (ArrayList<T> list) {
    list.add( (T) new Integer(4));
    sysout (list.get(0));
}

然后,如果我们将两个参数化的 ArrayList 中的每一个都传递给此方法的调用:

method(new ArrayList<Integer>());
method(new ArrayList<String>());

我们将看到两者都不会产生错误,并且都打印 4。我假设编译器保留了 as 的擦除。这个实验难道不能证明类型参数没有传递给方法吗?TObject

Java 泛型参数 传递 by-value

评论

0赞 user207421 3/4/2021
T是无界的,所以你真正拥有的是一个.这就是编译代码的原因。ArrayList<Object>
0赞 Benjamin M 3/4/2021
Java 编译器只是在编译时检查泛型。然后他们被移除了。尽管在 JVM 中运行的字节码不知道您使用的泛型类型。我想这可能是因为兼容性,因为在 Java 5 之前只有(没有泛型类型,因为当时不存在泛型),而当 Java 5 发布时,它变成了 ,但你今天仍然可以使用(没有泛型)事件,并且只收到一些编译器警告。ListList<T>List

答:

1赞 Ken Wayne VanderLinde 3/4/2021 #1

an 的情况实际上与普通的情况相同。在这两种情况下,在编译时用于静态类型检查。但是,在运行时,会被擦除。ArrayList<T>TTT

查看您发布的代码,编译后的方法是等效的:

void method (ArrayList<Object> list) {
    list.add( (Object) new Integer(4));
    sysout (list.get(0));
}

调用实际上是:

method(new ArrayList<Object>());
method(new ArrayList<Object>());

因此,从运行时的角度来看,您存储在 中,这很好。ObjectArrayList<Object>

现在,如果没记错的话,您应该会收到有关表达式 的未选中强制转换的警告。这是一个很好的警告,需要注意!如果目标类型与实际类型不兼容,则强制转换通常会引发异常。但是,在这种情况下,在运行时不存在,因此无法动态检查 - 强制转换是无操作的。但是,如果我们修改示例以返回强制转换的值,您可能会在运行时开始看到问题:(T) new Integer(4)T

<T> T method (ArrayList<T> list) {
    T value = (T) new Integer(4);
    list.add(value);
    return value;
}

然后

String result = method(new ArrayList<String>());

即使强制转换成功(这只是一个无操作),当返回值被赋值时,也会引发异常,因为该值是 an 而不是 .resultIntegerString

评论

0赞 Matthew S. 3/5/2021
那么,在我的示例中,我说我传递给方法的参数化对象的类型参数被剥离并替换为 ?你说“调用实际上是:Objectnew ArrayList<Object>"
0赞 Ken Wayne VanderLinde 3/5/2021
从本质上讲,是的。更准确地说,对象本身不是参数化的——它们在运行时创建之前不存在,而只在编译时存在。可以说在生成字节码之前已从表达式中删除。TT
0赞 Matthew S. 3/5/2021
“对象本身不是参数化的 - 它们在运行时创建之前不存在” 这是什么意思?你是说感知参数化类型是非规范化的,对象也不能参数化吗?请考虑以下代码段:最后一个代码段会导致错误。这难道不表明可以参数化对象(在编译时)吗?ArrayList<String> list = new ArrayList();list = new ArrayList<String>();list = new ArrayList<Integer>();
0赞 Ken Wayne VanderLinde 3/6/2021
由于对象在编译时(仅在运行时)不存在,因此不,它不会显示这一点。不要将对象(运行时)与表达式(编译时)混淆。表达式被编译为运行时创建对象的指令。表达式(和其他语法元素)可以参数化,但对象不能参数化。new
1赞 ernest_k 3/4/2021 #2

This is just a simple case where the compiler isn't enabled to force you to use within certain bounds. It's a bit as if your method doesn't care about as a type, as you noticed.Tmethod()T

The code is compiling, but with a warning. Your unchecked cast to is not always without consequences:T

ArrayList<String> strings = new ArrayList<>();
method(strings);
System.out.println(strings.get(0));

And:

Exception in thread "main" java.lang.ClassCastException: 
    java.lang.Integer cannot be cast to java.lang.String

This exception is raised by , because this call to is linked to the overload taking a String, and the cast doesn't pass.System.out.println(strings.get(3));println

That means that although doesn't check the cast to (at compile time or at runtime), the caller does. In my method, is inferred as and the runtime is able to perform the relevant type checks.method()Tmain()TString

I assume that the compiler retains the erasure of as .TObject

Roughly speaking, yes. But the compiler would be able to enforce more type safety in other cases, where is bounded. The following won't even compile:T

static <T extends String> void method(ArrayList<T> list) {
    list.add((T) new Integer(4));
    System.out.println(list.get(0));
}

So, "Does this experiment not prove that type arguments aren't passed to a method?" No. This is just one scenario of many.

评论

0赞 Matthew S. 3/4/2021
So if the answer is no, can you provide an example where you pass a parameterized object to my example method that shows this? In your first example you use sysout after you pass it to the method, which is the reason for the produced error because in the sysout you are using a parameterized type that has type argument has type arguments (also because of the print stream like you said). In your second example you use a bounded type parameter. If the answer is no, then you must be saying type arguments are retained when an object is passed to a method. Please show example so I understanstrings
0赞 Matthew S. 3/4/2021
I can also think of examples where I can do the same thing with a bounded type parameter. is the parent of both and so I could assign values to a that aren't compatible with NumberIntegerDoubleDoubleInteger
0赞 ernest_k 3/4/2021
The way I understand "passed to a method" is "the concrete type argument is 'seen' and enforced in the method". If I misunderstand, please clarify. The type isn't "passed". The compiler just enforces it after doing type inferences.
0赞 Matthew S. 3/4/2021
Yes that's what I mean. But, atleast in my example, the compiler seems to be enforcing the type erasure of and not the type arguments of the two parameterized objects I passed to the method. Is it possible to get the compiler to enforce the type arguments of an object or object reference passed to a method or are the type arguments stripped and replaced with what the compiler infers?T
0赞 Matthew S. 3/4/2021
Th other answer said that in my example the type arguments of the two objects I passed to the method become due to the erasure of ObjectT