Why couldn't the diamond operator originally be used with anonymous classes, and what changed in Java 9?
answer
- Anonymous class = implicit named subclass needing a concrete type arg
- Pre-9 ban: inference could yield a non-denotable (unnameable) type
- Non-denotable = intersection types, wildcard capture (CAP#1)
- Java 9 / JEP 213: allowed when inferred type is denotable
- Still an error if inference yields a non-denotable type
basics
~20 sIn Java 7-8 you couldn't write the diamond <> when creating an anonymous class, because the compiler couldn't always name the inferred type for the hidden subclass it generates. Java 9 relaxed this: it now allows <> with anonymous classes whenever the inferred type can be expressed.
solid answer
~50 sAn anonymous class creates an implicit subclass at the `new` site. With generics, the compiler infers the type argument and would use it as the type of that synthetic subclass. In Java 7-8 the diamond was simply disallowed for anonymous classes: the inferred type could be a non-denotable type (one with no name in Java's syntax, such as an intersection or a captured wildcard type), and the spec couldn't always represent it, so the language conservatively rejected all anonymous-class diamonds. You had to spell the type argument out: `new Comparator<String>() { ... }`. Java 9 (JEP 213) refined this: the diamond is now permitted with anonymous classes whenever the inferred type argument is denotable — i.e., can be written as a normal type. If inference would produce a non-denotable type, it remains an error and you must specify the argument explicitly. So in practice `new ArrayList<>() { ... }` and `new Comparator<>() { ... }` now compile.
code
java · 12 lines// Java 7-8: ERROR — diamond not allowed with anonymous class
// Comparator<String> c1 = new Comparator<>() { ... };
// Java 7-8: you had to spell out the type argument
Comparator<String> c2 = new Comparator<String>() {
public int compare(String a, String b) { return a.length() - b.length(); }
};
// Java 9+: diamond now works because String is a denotable type
Comparator<String> c3 = new Comparator<>() {
public int compare(String a, String b) { return a.length() - b.length(); }
};go deeper
Aware that older Java rejected new Foo<>(){...} and modern Java accepts it; not expected to explain why.
Knows Java 9 enabled the diamond on anonymous classes and that you previously had to spell the type out.
Explains the non-denotable-type reason for the original ban and that Java 9 allows it only when the inferred type is denotable.
Connects this to the gap between the compiler's internal type system and Java's surface syntax, and can reason about when inference yields non-denotable types.
## Background: what an anonymous class is An **anonymous class** is a class with no name, declared and instantiated in one expression, that implicitly extends a class or implements an interface: ```java Comparator<String> c = new Comparator<String>() { public int compare(String a, String b) { return a.length() - b.length(); } }; ``` Under the hood the compiler generates a hidden subclass (e.g. `Outer$1`) and creates an instance of it. With generics, that synthetic subclass needs a concrete type argument — here `String`. ## Why the diamond was banned (Java 7-8) When you use the diamond, the compiler **infers** the type argument. For an anonymous class, the inferred type becomes the type argument of the generated subclass. The problem: inference can produce a **non-denotable type** — a type that exists in the compiler's type system but has **no way to be written in Java source**. Examples of non-denotable types are: - **Intersection types** like `Serializable & Comparable<...>` produced by combining bounds. - **Capture types** from wildcard capture (the compiler's internal `CAP#1` placeholders). If inference for the anonymous-class diamond produced such a type, the compiler would need to give the synthetic subclass a type it literally cannot name. Rather than handle this partially, Java 7 took the **conservative** route and forbade the diamond with anonymous classes entirely. So this was a compile error in Java 7-8: ```java Comparator<String> c = new Comparator<>() { ... }; // error before Java 9 ``` and you were forced to repeat the type: ```java Comparator<String> c = new Comparator<String>() { ... }; ``` ## What Java 9 changed (JEP 213, 'Milling Project Coin') Java 9 refined the rule instead of removing it. The diamond is now allowed with anonymous classes **when the inferred type argument is denotable** — i.e., can be written as an ordinary type name. The compiler does the inference; if the result is a normal type, it accepts the diamond: ```java // Java 9+ — compiles, infers String Comparator<String> c = new Comparator<>() { public int compare(String a, String b) { return a.length() - b.length(); } }; ``` If inference would yield a non-denotable type (an intersection or capture), the diamond is **still** rejected and you must specify the argument explicitly. So Java 9 didn't make it unconditional — it allowed the common, safe cases and kept the error only for the genuinely unrepresentable ones. ## Why it matters This is mostly an ergonomics fix: with Java 8 lambdas you'd usually avoid anonymous classes anyway, but for cases that *must* be anonymous classes (e.g. you need fields, multiple methods, or you're subclassing a class not a functional interface) the diamond now removes the repeated type argument. Understanding the 'denotable type' restriction also illuminates a deeper concept: the compiler's internal types are richer than what Java syntax can express, which is exactly why some inference results can't be used in every position. ## Key takeaways - Anonymous class = implicit named subclass needing a concrete type argument. - Java 7-8 banned the diamond there because inference could yield a non-denotable (unnameable) type. - Java 9 allows it when the inferred type is denotable; otherwise it's still an error and you specify the type.
- What is a 'non-denotable type' and how does it relate to this restriction?A type the compiler can represent internally but that has no Java source syntax to name it — e.g. an intersection type or a wildcard capture (CAP#1). The pre-9 ban existed because anonymous-class diamond inference could produce one; Java 9 allows the diamond only when the inferred type is denotable.
- Can you use the diamond with a lambda?No — a lambda is not a class instantiation with type arguments, so there's nothing to put a diamond on. The diamond applies to constructor calls (new), including anonymous-class creation since Java 9.
saying these in an interview costs you the question
- Saying Java 9 made the anonymous-class diamond unconditionally legal (it's only when the type is denotable)
- Thinking the original ban was arbitrary rather than about non-denotable types
- Confusing anonymous classes with lambdas (lambdas can't use a diamond — they're not class instantiations)
- Believing the diamond changes which subclass is generated at runtime