GOT*_*O 0 25 java variadic-functions jls multiple-bounds
我想更好地理解当Java编译器遇到如下方法的调用时会发生什么.
<T extends AutoCloseable & Cloneable>
void printType(T... args) {
System.out.println(args.getClass().getComponentType().getSimpleName());
}
// printType() prints "AutoCloseable"
Run Code Online (Sandbox Code Playgroud)
我很清楚,<T extends AutoCloseable & Cloneable>在运行时没有类型,因此编译器可以做出最少的错误,并创建一个类型为两个边界接口之一的数组,丢弃另一个.
无论如何,如果切换接口的顺序,结果仍然是相同的.
<T extends Cloneable & AutoCloseable>
void printType(T... args) {
System.out.println(args.getClass().getComponentType().getSimpleName());
}
// printType() prints "AutoCloseable"
Run Code Online (Sandbox Code Playgroud)
这导致我做了一些调查,看看接口发生变化时会发生什么.在我看来,编译器使用某种严格的顺序规则来决定哪个接口是最重要的,并且接口在代码中出现的顺序不起作用.
<T extends AutoCloseable & Runnable> // "AutoCloseable"
Run Code Online (Sandbox Code Playgroud)
<T extends Runnable & AutoCloseable> // "AutoCloseable"
Run Code Online (Sandbox Code Playgroud)
<T extends AutoCloseable & Serializable> // "Serializable"
Run Code Online (Sandbox Code Playgroud)
<T extends Serializable & AutoCloseable> // "Serializable"
Run Code Online (Sandbox Code Playgroud)
<T extends SafeVarargs & Serializable> // "SafeVarargs"
Run Code Online (Sandbox Code Playgroud)
<T extends Serializable & SafeVarargs> // "SafeVarargs"
Run Code Online (Sandbox Code Playgroud)
<T extends Channel & SafeVarargs> // "Channel"
Run Code Online (Sandbox Code Playgroud)
<T extends SafeVarargs & Channel> // "Channel"
Run Code Online (Sandbox Code Playgroud)
<T extends AutoCloseable & Channel & Cloneable & SafeVarargs> // "Channel"
Run Code Online (Sandbox Code Playgroud)
问题: 当存在多个边界时,Java编译器如何确定参数化类型的varargs数组的组件类型?
我甚至不确定JLS是否对此有所说明,而且我通过谷歌搜索发现的任何信息都没有涵盖这一特定主题.
She*_*epy 12
通常,当编译器遇到对参数化方法的调用时,它可以推断出类型(JSL 18.5.2)并且可以在调用者中创建正确类型的vararg数组.
规则主要是说"找到所有可能的输入类型并检查它们"的技术方法(例如void,三元运算符或lambda).其余的是常识,例如使用最具体的公共基类(JSL 4.10.4).例:
public class Test {
private static class A implements AutoCloseable, Runnable {
@Override public void close () throws Exception {}
@Override public void run () {} }
private static class B implements AutoCloseable, Runnable {
@Override public void close () throws Exception {}
@Override public void run () {} }
private static class C extends B {}
private static <T extends AutoCloseable & Runnable> void printType( T... args ) {
System.out.println( args.getClass().getComponentType().getSimpleName() );
}
public static void main( String[] args ) {
printType( new A() ); // A[] created here
printType( new B(), new B() ); // B[] created here
printType( new B(), new C() ); // B[] which is the common base class
printType( new A(), new B() ); // AutoCloseable[] - well...
printType(); // AutoCloseable[] - same as above
}
}
Run Code Online (Sandbox Code Playgroud)
AutoCloseable & Channel简化为Channel.但规则无助于回答这个问题.AutoCloseable[]当然,从调用中获取可能看起来很奇怪,因为我们无法使用Java代码执行此操作.但实际上实际类型并不重要.在语言层面,args是T[],T"虚拟类型" 在哪里是A和B(JSL 4.9).
编译器只需要确保它的用法满足所有约束,然后它就知道逻辑是合理的,并且不存在类型错误(这就是Java泛型的设计方式).当然编译器仍然需要创建一个真正的数组,并且为此目的它创建了一个"通用数组".因此警告" unchecked generic array creation"(JLS 15.12.4.2).
换句话说,只要你只能传递AutoCloseable & Runnable,并要求只Object,AutoCloseable和Runnable的方法printType,实际的数组类型并不重要.实际上,printType无论传入何种类型的数组,其字节码都是相同的.
既然printType不关心vararg数组类型,getComponentType()不会也不应该重要.如果你想获得接口,请尝试getGenericInterfaces()返回一个数组.
T确实会影响(JSL 13.1)编译的方法签名和字节码.AutoClosable将使用第一个接口,例如,在AutoClosable.close()调用时不会进行类型检查printType.AutoClosable[]创建和传递.在擦除之前检查许多类型的安全装置,因此顺序不影响类型安全性.我认为这是JSL意味着的一部分"The order of types... is only significant in that the erasure ... is determined by the first type"(JSL 4.4).这意味着订单无关紧要.printType(AutoCloseable[])触发器编译错误,而添加printType( Runnable[])则不会.我认为这是一个意想不到的副作用,实际上超出了范围.| 归档时间: |
|
| 查看次数: |
464 次 |
| 最近记录: |