When does diamond syntax not work in Java 8?
Asked Answered
I

1

14

As of Java 7, diamond syntax did not always work in method arguments, e.g. Why does the diamond operator not work for java.util.Collections methods in Java 7? . The answer to that question mentions that target type inference in Java 8 fixes that issue.

Are there any remaining cases where diamond syntax cannot be used?

Irrepressible answered 25/11, 2014 at 19:2 Comment(1)
Not the diamond operator specifically, but here's a weakness of Java 8's type inference for lambda return types: https://mcmap.net/q/829331/-type-inference-on-lambdas/502399Scarberry
C
23

The diamond operator cannot always be used in Java 8. The original plan to improve inference in Java 8 (JEP 101) had two goals:

  • Add support for method type-parameter inference in method context
  • Add support for method type-parameter inference in chained calls

Only the first was implemented. Borrowing the example from the JEP, consider the following class:

class List<E> {
   static <Z> List<Z> cons(Z head, List<Z> tail) { ... };
   E head() { ... }
}

In Java 8, the improved method context inference allows the following to compile. With Java 7, it would fail with the error expected List<Integer>, found List<Object>

List<Integer> l = List.cons(42, new List<>());

However, examples that require inference of chained calls still don't work with Java 8:

Integer i = new List<>().head();

Section D of JSR 335 includes a hint about why chained expression inference was abandoned for Java 8:

There has been some interest in allowing inference to "chain": in a().b(), passing type information from the invocation of b to the invocation of a. This adds another dimension to the complexity of the inference algorithm, as partial information has to pass in both directions; it only works when the erasure of the return type of a() is fixed for all instantiations (e.g. List). This feature would not fit very well into the poly expression model, since the target type cannot be easily derived; but perhaps with additional enhancements it could be added in the future.


There are also some more contrived examples where diamond can't be used.

If bugs count, this doesn't compile with javac prior to jdk8u25. (see JDK-8029002)

class Test {
  class C<T extends C<T>> {}
  void m() {
    C<?> i = new C<>();
  }
}

error: incompatible types: cannot infer type arguments for Test.C<>
    C<?> i = new C<>();
                  ^
    reason: inferred type does not conform to upper bound(s)
      inferred: Test.C<CAP#1>
      upper bound(s): Test.C<Test.C<CAP#1>>,Test.C<CAP#1>
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Test.C<CAP#1> from capture of ?

There's also a performance issue (JDK-8051946) with the new type inference implementation that can affect code using the diamond operator. The following example takes minutes to compile if the diamond operator is used.

class Test {
  <T> T and(T a, T b) { return null; }
  class C<T> {}

  void g(String s) {}
  void g(Object s) {}

  void m() {
    g(
        and(
        and(
        and(
        and(
        and(
        and(
        and(new C<>(),
            new C<>()),
            new C<>()),
            new C<>()),
            new C<>()),
            new C<>()),
            new C<>()),
            new C<>()));
  }
}
Clipclop answered 26/11, 2014 at 21:9 Comment(1)
Plus one; with answers like this you'll overtake Jon Skeet.Attemper

© 2022 - 2024 — McMap. All rights reserved.