I'm currently facing a problem with Lombok's @SuperBuilder
annotation and generics. I have an abstract
parent and a final
child class. When creating the child using the builder, passing values of the correct type leads to compile errors.
The simplified classes and interfaces
@SuperBuilder(toBuilder = true)
public abstract class Parent<T> {
private SomeGenericClass<?, Parent<T>> someGenericClass;
private T value;
}
interface SomeInterface<D> {
}
class SomeGenericClass<D, S> implements SomeInterface<D> {
}
@SuperBuilder(toBuilder = true)
final class Child<T> extends Parent<T> {
}
The test code
public class SuperBuilderTest {
public static void main(String[] args) {
SomeGenericClass<String, Parent<Long>> someGenericClass = new SomeGenericClass<>();
Child<Long> child = Child.builder()
.someGenericClass(someGenericClass) // error: The method someGenericClass(SomeGenericClass<?,Parent<Object>>) in
// the type Parent.ParentBuilder<Object,capture#1-of ?,capture#2-of ?> is
// not applicable for the arguments (SomeGenericClass<String,Parent<Long>>)
.value(10L) // error: Type mismatch: cannot convert from capture#1-of ? to Child<Long>
.build();
}
}
If works, as soon as the child itself no longer has a generic parameter. So having the child like this, everything compiles and works perfectly fine:
@SuperBuilder(toBuilder = true)
final class Child extends Parent<Long> {
}
Am I doing something wrong or is it not possible to have generic final
classes when using the @SuperBuilder
?
The type parameter for builder()
(which represents what kind of Child
you are creating) is not inferred correctly. The compiler thinks it's Object
. To fix the error, just make it very clear what kind of Child.Builder
you are creating, e.g.
Child.<Long>builder()
...
The reason for why this cannot be inferred is similar to why chaining thenComparing
s breaks type inference.
You can see something similar happen with simple builders too:
@Builder
final class Foo<T> {
private T t;
}
// error here!
Foo<Long> f = Foo.builder().t(1L).build();
Though in simple cases like that, the error message is a lot clearer, saying that you cannot convert a Foo<Object>
to Foo<Long>
.
Your case is more complicated - you have a SomeGenericClass<?, Parent<T>>
field. That means someGenericClass()
is expecting a SomeGenericClass<?, Parent<Object>>
. A SomeGenericClass<String, Parent<Long>>
certainly can't be passed to that. And the fact that you're using SuperBuilder
makes things even more complicated.