While programming in Java, I encountered the following problem.
when use Constructor: The following code works fine.
public class Generics<T> {
private T data;
public static <T> Generics<T> of(T data) {
return new Generics<>(data);
}
public Generics(T data) {
this.data = data;
}
}
when use builder: An error occurs saying that the Object type is provided as follows.
I used a builder provided by Project Lombok.
Why doesn't the builder generic in the code above work?
CodePudding user response:
Evidently Lombok generates a static generic builder()
method. You can specify the generic type of a generic static method using <T>
before the method name, as in:
return Generics.<T>builder()
.data(data)
.build();
If you don't specify the generic type when you call builder()
, you get a raw type, at which point generic type inference no longer works.
CodePudding user response:
You need to explicitly add the generics: return Generics.<T>builder()
- add that <T>
and all will be well.
Explanation
Java is aggressively typed: Every expression and just about every part of an expression has an actual type; java does not allow things to remain in limbo until later; there's no such thing as 'an intermediate - eh, we'll see where it goes'.
That means Generics.builder()
, as an expression, needs to be typed by the system. The builder class needs that <T>
just the same (it's a Generics.GenericsBuilder<X>
- where Builder
is a static inner class of Generics, and is defined as public static class GenericsBuilder<X>
.
Java cannot just jump to the conclusion that you intend for X and T to be the same type.
Looking ahead, it can figure out that X should be T 'automatically', without your involvement: It can either check the .data(data)
invocation, given that the data
method is defined in the builder as:
public GenericsBuilder<X> data(X data)
thus, whatever type data
(the variable)might be (it's
There, given that it's the parameter defined as
T data`), and therefore, java can conclude that the X should be T.
Otherwise, java can look even further ahead, to build()
, and notice that this returns X
, and is being returned by a method whose return type is defined as T
, thus also giving java the opportunity to say: Ah, X == T, right.
But that is not how java works. In large part because even figuring out what the .data
method might mean is rather hard to do when the X
of that builder you just made (the X
in the declaration public static <X> Generics.GenericsBuilder<X> builder()
) must be left in some sort of unknown limbo state for a while as the parser carries on with trying to figure out what the rest means. Given that java allows method overloading (2 different methods with the same name and the same number of params, but different param types, which could contain generics to boot) - it'd be a combinatorial explosion and means you can write java code that takes literally years to parse.
Hence, java does not work that way, it must determine what X
is supposed to be solely from the expression Generics.builder()
, and it clearly cant.
The solution is to just write it explicitly, using this somewhat exotic syntax: Generics.<T>builder()
.