Class
MyEntity
扩展了基类。两者都有 lombok(版本 1.18.34)@SuperBuilder
注释。
@Getter
@Setter
@SuperBuilder
@ToString(onlyExplicitlyIncluded = true)
@EqualsAndHashCode
public abstract class BaseEntity {
public abstract SomeType getSomeType();
}
@Value
@SuperBuilder
@ToString(onlyExplicitlyIncluded = true)
@EqualsAndHashCode(callSuper = true)
public class MyEntity extends BaseEntity {
@Override
public SomeType getSomeType() {
return SomeType.SOMETHING;
}
}
如果我尝试像这样将某些内容映射到 MyEntity
someItems.stream()
.map(item -> MyEntity.builder().build())
.toList();
其返回类型是
List<?>
。为什么编译器无法推断出这里的类型?如果我查看 build()
方法,我确实会看到一个通用的 public abstract C build()
。private MyEntity getBuild(SomeItem item) {
return MyEntity.builder().build();
}
List<MyEntity> myEntities = someItems.stream() // works
.map(item -> getBuild(item))
.toList();
toList
调用的返回类型不是List<?>
,而是List<T>
,其中T
是上限为MyEntity
的新类型变量。这意味着您可以将其分配给类型为 List<? extends MyEntity>
的变量。
// this compiles
List<? extends MyEntity> result = someItems.stream()
.map(item -> MyEntity.builder().build())
.toList();
发生这种情况的原因是因为
builder()
返回 MyEntityBuilder<?, ?>
。重要的是,它的第一个类型参数(类型C
,也是build
的返回类型)是一个通配符,所以我们对C
一无所知,除了它继承自MyEntity
之外。然后,这个 ?
经过捕获转换,成为一个具有上限 MyEntity
的新类型变量。我们将新鲜类型变量称为 T
。
这意味着
build
将返回 T
。所以 map
返回 Stream<T>
,toList
返回 List<T>
。
由于
T
的上限为 MyEntity
,因此可以将 T
转换为 MyEntity
。但在 T
放入列表之前,您的代码永远不会执行此操作,此时不再可能隐式执行此操作(List<? extends MyEntity>
不是 List<MyEntity>
的子类型)。
您可以在
T
lambda 中提前进行 MyEntity
-> map
转换:
List<MyEntity> result = Stream.of(1)
.map(item -> (MyEntity)MyEntity.builder().build())
.toList();