让我们说我有两个类和两个方法:
class Scratch {
private class A{}
private class B extends A{}
public Optional<A> getItems(List<String> items){
return items.stream()
.map(s -> new B())
.findFirst();
}
public Optional<A> getItems2(List<String> items){
return Optional.of(
items.stream()
.map(s -> new B())
.findFirst()
.get()
);
}
}
为什么getItems2
编译时getItems
给出编译器错误
incompatible types: java.util.Optional<Scratch.B> cannot be converted to java.util.Optional<Scratch.A>
因此,当我get
返回Optional
的值findFirst
并再次用Optional.of
包装时,编译器会识别继承,但是如果我直接使用结果,则不会的findFirst
。
答案 0 :(得分:21)
Optional<B>
不是Optional<A>
的子类型。与其他编程语言不同,Java的泛型类型系统不知道“只读类型”或“输出类型参数”,因此它不了解Optional<B>
仅提供B
的实例并且可以在需要Optional<A>
的地方。
当我们写这样的语句
时Optional<A> o = Optional.of(new B());
Java的类型推断使用目标类型来确定我们想要的类型
Optional<A> o = Optional.<A>of(new B());
在需要new B()
实例的情况下,可以使用有效为A
的。
这同样适用于
return Optional.of(
items.stream()
.map(s -> new B())
.findFirst()
.get()
);
其中方法的声明的返回类型用于推断Optional.of
调用的类型参数,并传递get()
的结果,B
的实例,其中A
是必需的,有效。
不幸的是,此目标类型推断无法通过链式调用进行,因此
return items.stream()
.map(s -> new B())
.findFirst();
它不用于map
调用。因此,对于map
调用,类型推断将使用new B()
的类型,其结果类型将为Stream<B>
。第二个问题是findFirst()
不是通用的,在Stream<T>
上调用它总是会产生Optional<T>
(而Java的通用名称不允许声明<R super T>
之类的类型变量,因此甚至不可能在此处生成具有所需类型的Optional<R>
。
→解决方案是为map
调用提供显式类型:
public Optional<A> getItems(List<String> items){
return items.stream()
.<A>map(s -> new B())
.findFirst();
}
仅出于完整性考虑,findFirst()
不是通用的,因此不能使用目标类型。链接允许类型更改的通用方法也可以解决问题:
public Optional<A> getItems(List<String> items){
return items.stream()
.map(s -> new B())
.findFirst()
.map(Function.identity());
}
但是我建议使用为map
调用提供显式类型的解决方案。
答案 1 :(得分:8)
您遇到的问题是泛型继承。 Optional 不扩展Optional ,因此不能这样返回。
我想像这样的东西:
public Optional<? extends A> getItems( List<String> items){
return items.stream()
.map(s -> new B())
.findFirst();
}
或者:
public Optional<?> getItems( List<String> items){
return items.stream()
.map(s -> new B())
.findFirst();
}
根据您的需求可以正常工作。
编辑:转义一些字符
答案 2 :(得分:6)
Optional<B>
不是Optional<A>
的子类。
在第一种情况下,您有一个Stream<B>
,因此findFirst
返回一个Optional<B>
,该值不能转换为Optional<A>
。
在第二种情况下,您具有返回B
实例的流管道。当您将该实例传递给Optional.of()
时,编译器会发现该方法的返回类型为Optional<A>
,因此Optional.of()
返回一个Optional<A>
(因为Optional<A>
可以保存B
的实例作为其值(因为B
扩展了A
)。
答案 3 :(得分:3)
看看这个类似的例子:
Optional<A> optA = Optional.of(new B()); //OK
Optional<B> optB = Optional.of(new B()); //OK
Optional<A> optA2 = optB; //doesn't compile
您可以通过将第二种方法重写为:
public Optional<A> getItems2(List<String> items) {
return Optional.<B>of(items.stream().map(s -> new B()).findFirst().get());
}
这仅仅是因为泛型类型是不变的。
为什么有区别?请参见Optional.of
的声明:
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
可选的类型是从分配的目标中选取的(在这种情况下为返回类型)。
还有Stream.findFirst()
:
//T comes from Stream<T>, it's not a generic method parameter
Optional<T> findFirst();
但是,在这种情况下,return items.stream().map(s -> new B()).findFirst();
不会基于声明的返回类型.findFirst()
输入getItems
的结果(T
严格基于类型Stream<T>
的参数)
答案 4 :(得分:2)
如果类B继承了类A,则并不意味着Optional继承了Optional。可选的是另一个类。