我正在摸索这个例子是如何运作的,并且似乎打印得恰到好处。
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的有界通配符类型做了什么?
答案 0 :(得分:3)
基本上,即使您删除了类型,也可以使用以下内容:
....
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,在你的情况下是覆盖的。因此,在Object类型变量上调用它永远不会失败,在您的情况下 - 调用overriden方法。
已添加:
现在,如果你在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确实有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并不在乎。它希望在编译期间它正确地发现了所有错误,因此运行期间不会发生这样的错误(扰乱:无论如何,它们确实会发生,如果一个人不小心的话,时间会发生)。