简体   繁体   English

具有绑定通配符类型的可变参数的方法如何编译?

[英]How does a method with a varargs of Bounded Wildcard type compile?

I'm scratching my head over how this example works and seems to print appropriately . 我对这个示例的工作方式摸不着头脑,而且似乎可以正确打印。

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);
    }
}

Calling tests will print: 呼叫测试将打印:

"Shape"
"Square"
"Circle"

Intuitively this makes sense, as the method signature is described as accepting any amount of arguments as long as they are of type ThreadLocal with a type of any extension of Shape. 直观上讲,这是有道理的,因为方法签名被描述为接受任意数量的参数,只要它们的类型为ThreadLocal且具有Shape的任何扩展名即可。 So passing in a ThreadLocal<Square> along with ThreadLocal<Circle> fits the bill. 因此,将ThreadLocal<Square>ThreadLocal<Circle>一起传递就可以了。

But how does this compile in such a way that runtime can determine the proper overload of string? 但是,如何以运行时可以确定适当的字符串重载的方式进行编译? My hazy understanding of generic type erasure makes it seem like this sort of method signature should not be possible to even compile. 我对泛型类型擦除的朦胧理解使这种方法签名似乎甚至无法编译。 I would've thought that the signature for wildCardVarArgs becomes something along the lines of wildCardVarArgs(ThreadLocal[]) in byte code, which seems like execution should run into a ClassCastException. 我本以为wildCardVarArgs的签名在字节码中变成了wildCardVarArgs(ThreadLocal[])样子,似乎执行应该遇到ClassCastException。 But the more I think about it, the more confused I get. 但是我想得越多,我就越困惑。

Anyone able to make sense of this and what does compiling do to a Bounded Wildcard type of ThreadLocal? 任何人都可以理解这一点,并且编译对ThreadLocal的Bounded Wildcard类型有什么作用?

Basically, even if you remove the types, here's what you get: 基本上,即使删除类型,也可以得到以下信息:

....
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"
    }
}
....

So, whatever you push in it, the base class of everything, that is Object, has a method toString, which in your case is overriden. 因此,无论您输入什么内容,所有对象的基类(即Object)都具有toString方法,在您的情况下,该方法将被覆盖。 Therefore, calling it on Object type variable never fails, and in your case - calls the overriden method. 因此,在对象类型变量上调用它永远不会失败,并且在您的情况下-调用重写方法。


Added: 添加:

Now, in case you add some new method into the Shape class, here is what happens: 现在,如果您将一些新方法添加到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
    }
}

Given this code, compiler knows for sure, that whichever ThreadLocal you give to this method, it contains some kind of a Shape implementation. 有了这些代码,编译器就可以肯定地知道,无论您将此方法提供给哪种ThreadLocal,它都包含某种 Shape实现。 It does not actually care which one it is, however, it can guarantee that you didn't give it anything but some kind of a Shape . 它实际上并不关心它是哪一个,但是,它可以保证除了某种Shape之外 ,您什么都不给。 Therefore, it can check at compile time, that Shape indeed has someCustomMethod on it. 因此,它可以在编译时检查Shape是否确实具有一些someCustomMethod Again, it does not care at this point if someone Overriden it or not, it just calls it. 再一次,它并不在乎此时是否有人重写它,它只是调用它。 If it was overriden - the overriden one is called. 如果被覆盖-被覆盖的被调用。 If it was not overriden - the original, from Shape class is called. 如果未覆盖-则调用Shape类中的原始对象。


More added: 更多添加:

I would've thought that the signature for wildCardVarArgs becomes something along the lines of wildCardVarArgs(ThreadLocal[]) in byte code, which seems like execution should run into a ClassCastException. 我本以为wildCardVarArgs的签名在字节码中变成了wildCardVarArgs(ThreadLocal [])的样子,似乎执行应该遇到ClassCastException。

That's exactly what happens (not exactly the array part, but the erasure). 这就是发生的情况(不是数组部分,而是擦除)。 That is, after the compilation all that remains is, basically: 也就是说,编译后剩下的基本上是:

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());
    }
}

However, Java does not really care. 但是,Java并不在乎。 It hopes that during the compile time it found all the errors correctly, therefore such errors during runtime should not happen (spoiler: they do happen anyway, time from time, if one is not careful). 它希望在编译时能正确发现所有错误,因此不应在运行时发生此类错误(破坏者:如果有人不小心,它们确实会不时发生)。

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

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