繁体   English   中英

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

[英]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.

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