[英]How does a method with a varargs of Bounded Wildcard type compile?
我对这个示例的工作方式摸不着头脑,而且似乎可以正确打印。
public class Test {
static class Shape {
public String toString() {
return "Shape";
}
}
static class Circle extends Shape {
public String toString() {
return "Circle";
}
}
static class Square extends Shape {
public String toString() {
return "Square";
}
}
public static void wildCardVarArgs(ThreadLocal<? extends Shape>... list) {
for (ThreadLocal<? extends Shape> s : list) {
System.out.println(s.get().toString());
}
}
public static void test() {
ThreadLocal<Shape> shape = new ThreadLocal<>();
shape.set(new Shape());
ThreadLocal<Square> square = new ThreadLocal<>();
square.set(new Square());
ThreadLocal<Circle> circle = new ThreadLocal<>();
circle.set(new Circle());
wildCardVarArgs(shape, square, circle);
}
}
呼叫测试将打印:
"Shape"
"Square"
"Circle"
直观上讲,这是有道理的,因为方法签名被描述为接受任意数量的参数,只要它们的类型为ThreadLocal且具有Shape的任何扩展名即可。 因此,将ThreadLocal<Square>
与ThreadLocal<Circle>
一起传递就可以了。
但是,如何以运行时可以确定适当的字符串重载的方式进行编译? 我对泛型类型擦除的朦胧理解使这种方法签名似乎甚至无法编译。 我本以为wildCardVarArgs的签名在字节码中变成了wildCardVarArgs(ThreadLocal[])
样子,似乎执行应该遇到ClassCastException。 但是我想得越多,我就越困惑。
任何人都可以理解这一点,并且编译对ThreadLocal的Bounded Wildcard类型有什么作用?
基本上,即使删除类型,也可以得到以下信息:
....
public static void wildCardVarArgs(ThreadLocal... list) {
for (ThreadLocal s : list) {
Object o = s.get(); //Compiler knows there's "at least" Object inside
System.out.println(o.toString()); //And all Objects have "toString"
}
}
....
因此,无论您输入什么内容,所有对象的基类(即Object)都具有toString方法,在您的情况下,该方法将被覆盖。 因此,在对象类型变量上调用它永远不会失败,并且在您的情况下-调用重写方法。
添加:
现在,如果您将一些新方法添加到Shape类中,将发生以下情况:
public static void wildCardVarArgs(ThreadLocal<? extends Shape>... list) {
for (ThreadLocal<? extends Shape> s : list) {
Shape o = s.get(); //Compiler knows there's "at least" Shape inside
System.out.println(o.someCustomMethod()); //And all Shapes have "someCustomMethod", does not matter if it is overriden or not
}
}
有了这些代码,编译器就可以肯定地知道,无论您将此方法提供给哪种ThreadLocal,它都包含某种 Shape实现。 它实际上并不关心它是哪一个,但是,它可以保证除了某种Shape之外 ,您什么都不给。 因此,它可以在编译时检查Shape是否确实具有一些someCustomMethod
。 再一次,它并不在乎此时是否有人重写它,它只是调用它。 如果被覆盖-被覆盖的被调用。 如果未覆盖-则调用Shape类中的原始对象。
更多添加:
我本以为wildCardVarArgs的签名在字节码中变成了wildCardVarArgs(ThreadLocal [])的样子,似乎执行应该遇到ClassCastException。
这就是发生的情况(不是数组部分,而是擦除)。 也就是说,编译后剩下的基本上是:
public static void wildCardVarArgs(ThreadLocal... list) {
for (ThreadLocal s : list) {
Object o = s.get();
Shape p = (Shape)o; //Java hopes this won't fail, because compiler checked that it should not
System.out.println(p.someCustomMethod());
}
}
但是,Java并不在乎。 它希望在编译时能正确发现所有错误,因此不应在运行时发生此类错误(破坏者:如果有人不小心,它们确实会不时发生)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.