Why can't diamond infer types on anonymous inner classes?

放肆的年华 提交于 2020-01-10 08:50:11

问题


In Java 7 and later, diamond can be used to infer types on normally like so without an issue:

List<String> list = new ArrayList<>();

However, it can't for anonymous inner classes like this:

List<String> st = new List<>() { //Doesn't compile

    //Implementation here

}

Why is this? Logically in this scenario, I can definitely infer the type as String. Is there a logical reason for this decision where the type cannot actually be inferred on anonymous inner classes, or was it omitted for other reasons?


回答1:


In the JSR-334:

Using diamond with anonymous inner classes is not supported since doing so in general would require extensions to the class file signature attribute to represent non-denotable types, a de facto JVM change.

What I guess is that as everybody knows, anonymous class leads to a generation of its own class file.

I imagine that generic type doesn't exist within these files and rather replaced by the effective (static) type (thus declared by the explicit type like <String> at declaration object time).

Indeed, file corresponding to an inner class is never shared across multiple different instantiations of it, so why bother with generics into it?! :).

It would be more hardly achievable (and surely useless) for compiler to force an extension (by adding a special attribute for generics) to theses kind of class files.




回答2:


google yields, after skipping posts from stackoverflow, http://mail.openjdk.java.net/pipermail/coin-dev/2011-June/003283.html

I'm guessing it's like this, usually an anonymous class is a concrete subclass of the apparent type

    interface Foo<N extends Number>
    {
        void foo(N n);
    }

    Foo<Integer> foo = new Foo<Integer>(){ ... }

is implemented by

    class AnonFoo_1 implements Foo<Integer>{ ... }

    Foo<Integer> foo = new AnonFoo_1();

Suppose we allow diamond inference on anonymous classes, there can be complicated case like

    Foo<? extends Runnable> foo = new Foo<>(){ ... }

The inference rules yield N=Number&Runnable; following the prev implementation trick, we need

    class AnonFoo_2 implements Foo<Number&Runnable>{ ... }

That is currently not allowed; the type arg to super type Foo must be a "normal" type.


However, the rationale is not very strong. We can invent other implementation tricks to make it work

    class AnonFoo<N extends Number&Runnable> implements Foo<N>
    {
        @Override public void foo(N n)
        {
            n.intValue();
            n.run();
        }
    }

    Foo<? extends Runnable> foo = new AnonFoo<>();

the compiler ought to be able to do the same trick.

In any case, at least the compiler should allow the majority of use cases that do not involve "undenotable types", like Foo<Integer> foo = new Foo<>(){...} It's a pity that these common/simple cases are unnecessarily forbidden too.




回答3:


In short, the <> does little to infer types, it turns off the warning you would get without it.

EDIT: As @Natix points out it does some checking.

List<Integer> ints = new ArrayList<>();
List<String> copy = new ArrayList<>(ints);

produces a compilation error

Error:Error:line (42)error: incompatible types
required: List<String>
found:    ArrayList<Integer>

As you can see the <> is taking the type of the argument, not inferring the type from the type of copy



来源:https://stackoverflow.com/questions/13821586/why-cant-diamond-infer-types-on-anonymous-inner-classes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!