Generic type inference limits in Java

G9QV2 :

I'm facing the following problem in my project with Java generics type inference. This is a code sample that's similar to my original one:

public class BuildableObject<R, S> {
  public static class OneParameter<R> { }
  public static class TwoParameters<R, S> { }
  interface TwoParamInterface<R, S> { }
  public static class Implementer<T> implements TwoParamInterface<T, T> {}

  private final OneParameter<R> first;
  private final OneParameter<S> second;
  private final TwoParameters<R, S> third;
  private final TwoParamInterface<R, S> fourth;

  private BuildableObject(OneParameter<R> first, OneParameter<S> second, TwoParameters<R, S> third, TwoParamInterface<R, S> fourth) {
    this.first = first;
    this.second = second;
    this.third = third;
    this.fourth = fourth;
  }

  public static class Builder<R, S> {
    private OneParameter<R> first = null;
    private OneParameter<S> second = null;
    private TwoParameters<R, S> third = null;
    private TwoParamInterface<R, S> fourth = null;

    public Builder() {}

    public Builder<R, S> first(OneParameter<R> first) {
      this.first = first; return this;
    }

    public Builder<R, S> second(OneParameter<S> second) {
      this.second = second; return this;
    }

    public Builder<R, S> third(TwoParameters<R, S> third) {
      this.third = third; return this;
    }

    public Builder<R, S> fourth(TwoParamInterface<R, S> fourth) {
      this.fourth = fourth; return this;
    }

    public BuildableObject<R, S> build() {
      return new BuildableObject<>(first, second, third, fourth);
    }
  }

  public static void main(String... args) {
    new Builder<>()
        .first(new OneParameter<>())
        .second(new OneParameter<>())
        .third(new TwoParameters<>())
        .fourth(new Implementer<String>())
        .build();
  }
}

This code breaks at new Implementer<String>, but works if I use new Builder<String, String> instead of new Builder<>.

Why can't Java infer that the type of the Builder is Builder<String, String> if the types of R and S are specified in new Implementer<String>?

What are the limits of Java generic types inference? Does it only resolve types provided in constructors or static methods? I haven't found any documentation on this.

Does this mean in any way that this class might not be type safe if we can't use type inference?

Alexey Romanov :

It's documented in detail in https://docs.oracle.com/javase/specs/jls/se9/html/jls-18.html. But the problem is that it's documented in detail: there is a lot of jargon you are unlikely to be familiar with unless you read papers on this subject.

For this case you just need to understand that for type inference it doesn't matter what methods you call after new Builder<>(); only parameters to the constructor itself are used (and a target type e.g. in Builder<String, String> b = new Builder<>();, but in this case you don't have one).

Does it only resolve types provided in constructors or static methods?

No.

Does this mean in any way that this class might not be type safe if we can't use type inference?

They are completely unrelated.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=461005&siteId=1