简体   繁体   中英

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

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?

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.

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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