24

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?

Michael Berry
  • 70,193
  • 21
  • 157
  • 216
  • 2
    @Philipp I disagree - that question is asking why a certain piece of code doesn't compile (indeed the answer is just that you can't use diamond with anonymous inner classes), this one is asking the technical / logical reason for *why* the Java devs chose to put that particular restriction in place. Related, but hardly the same thing. – Michael Berry Dec 11 '12 at 16:09
  • 5
    This has been vastly improved in JDK 9: https://bugs.openjdk.java.net/browse/JDK-8062373 – jcsahnwaldt Reinstate Monica Sep 01 '17 at 14:25
  • @MichaelBerry if your reopening was justified, please remove the link to the duplicate, otherwise please re-close the Question. – Cœur Jul 08 '18 at 16:58

3 Answers3

15

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.

Mik378
  • 21,881
  • 15
  • 82
  • 180
6

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.

irreputable
  • 44,725
  • 9
  • 65
  • 93
  • Your example is strictly applicable with a simple (non-anonymous) concrete class. What is the specific feature corresponding to anonymous class in your sample? Indeed, if your theory were true, does never diamond could be applicable whatever the case. – Mik378 Dec 11 '12 at 16:17
  • on bytecode level, I don't think there's anything special about anonymous classes. – irreputable Dec 11 '12 at 16:20
  • So your sample would behave in exactly the same manner if instead of having an interface `Foo` you have: class Foo { void foo(N n){} } Foo foo = new Foo(); – Mik378 Dec 11 '12 at 16:23
  • What I want to say is that even this line (with a simple concrete class instantiation) never compiles: `Foo extends Runnable> foo = new Foo??What I put in there???>();` => `Number` unrelated to `Runnable`. So the "HARD WORK" you're showing (declaring interface with multiple extending class) need obviously to be achieve for non-anonymous concrete classes also. – Mik378 Dec 11 '12 at 16:33
  • if you put nothing there, it'll compile:) – irreputable Dec 11 '12 at 16:36
  • Ok I get it :) Poor compiler ^^ – Mik378 Dec 11 '12 at 17:03
  • Actually, simple case compiles since it leads to a type check by compiler (doing intersection of both types) instead of creating a new class (like does it for anonymous class) constraining to declaring physically the multiple `extends` and therefore breaking the rules from `Foo` generics type allowed. – Mik378 Dec 11 '12 at 17:50
  • 3
    @Mik378 the difference between `new Foo<>()` and `new Foo<>() {}` is that the former is a generic instantiation, subject to type erasure, whereas the latter creates non-generic class extending a parameterized type that will be captured in byte code (you can call `getClass().getGenericSuperclass()` at runtime and see the actual type arguments). This will create problems with non-denotable types. In fact, these problems do already exist, e.g. `public class Foo { Foo(T t) {} public static void main(String... arg) { new Foo<>((Runnable&CharSequence)null).new Inner() {}; } class Inner {} }`. – Holger Sep 13 '20 at 16:10
1

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

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 3
    Not exactly, diamond still performs a type check. It may not be obvious for simple collection initializations, but take for example using a copy constructor: `List copy = new ArrayList<>(original);` This ensures that the `original` list is also a list of strings (or more precisely a `Collection extends String>`). – Natix Dec 11 '12 at 15:02
  • While this is true, it is not inferring the type from how it is used. It is taking the type from an argument. – Peter Lawrey Dec 11 '12 at 15:17