使用 Lombok 的 @SuperBuilder 在使用泛型的最终子类时无法编译。

huangapple go评论80阅读模式
英文:

Using Lombok @SuperBuilder Using Generic Final Child Class Does Not Compile

问题

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();
	}
}

It 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?

英文:

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&lt;T&gt; {

	private SomeGenericClass&lt;?, Parent&lt;T&gt;&gt; someGenericClass;
	private T value;
}

interface SomeInterface&lt;D&gt; {

}

class SomeGenericClass&lt;D, S&gt; implements SomeInterface&lt;D&gt; {

}

@SuperBuilder(toBuilder = true)
final class Child&lt;T&gt; extends Parent&lt;T&gt; {

}

The test code

public class SuperBuilderTest {

	public static void main(String[] args) {
		SomeGenericClass&lt;String, Parent&lt;Long&gt;&gt; someGenericClass = new SomeGenericClass&lt;&gt;();

		Child&lt;Long&gt; child = Child.builder() 
				.someGenericClass(someGenericClass) // error: The method someGenericClass(SomeGenericClass&lt;?,Parent&lt;Object&gt;&gt;) in
				// the type Parent.ParentBuilder&lt;Object,capture#1-of ?,capture#2-of ?&gt; is
				// not applicable for the arguments (SomeGenericClass&lt;String,Parent&lt;Long&gt;&gt;)
				.value(10L) // error: Type mismatch: cannot convert from capture#1-of ? to Child&lt;Long&gt;
				.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&lt;Long&gt; {

}

Am I doing something wrong or is it not possible to have generic final classes when using the @SuperBuilder?

答案1

得分: 3

builder() 方法的类型参数(表示您正在创建的 Child 的类型)没有被正确地推断出来。编译器认为它是 Object。要修复这个错误,只需明确指出您要创建的 Child.Builder 的类型,例如:

Child.<Long>builder()
    ...

不能正确推断的原因与链式调用 thenComparing 破坏了类型推断的原因类似。

对于简单的构建器也会出现类似的情况:

@Builder
final class Foo<T> {
    private T t;
}

// 出错在这里!
Foo<Long> f = Foo.builder().t(1L).build();

尽管在简单情况下,错误消息会更清晰,说明不能将 Foo<Object> 转换为 Foo<Long>

您的情况更加复杂 - 您有一个 SomeGenericClass<?, Parent<T>> 字段。这意味着 someGenericClass() 预期一个 SomeGenericClass<?, Parent<Object>>。一个 SomeGenericClass<String, Parent<Long>> 显然不能传递给它。而且您使用了 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.&lt;Long&gt;builder()
    ...

The reason for why this cannot be inferred is similar to why chaining thenComparings breaks type inference.

You can see something similar happen with simple builders too:

@Builder
final class Foo&lt;T&gt; {
    private T t;
}

// error here!
Foo&lt;Long&gt; 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&lt;Object&gt; to Foo&lt;Long&gt;.

Your case is more complicated - you have a SomeGenericClass&lt;?, Parent&lt;T&gt;&gt; field. That means someGenericClass() is expecting a SomeGenericClass&lt;?, Parent&lt;Object&gt;&gt;. A SomeGenericClass&lt;String, Parent&lt;Long&gt;&gt; certainly can't be passed to that. And the fact that you're using SuperBuilder makes things even more complicated.

huangapple
  • 本文由 发表于 2023年6月22日 15:20:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/76529436.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定