问题
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