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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With