正如Bounding generics with 'super' keyword中所讨论的,当涉及方法泛型的下限时,Java类型系统被破坏/不完整。由于Optional现在是JDK的一部分,我开始更多地使用它,并且Guava遇到的问题与他的可选性开始变得困难。我提出了一个体面的工作,但我不确定它是否安全。首先,让我设置一个例子:
public class A {}
public class B extends A {}
我希望能够声明一个方法:
public class Cache {
private final Map<String, B> cache;
public <T super B> Optional<T> find(String s) {
return Optional<T>.ofNullable(cache.get(s));
}
}
以下两项工作都是:
A a = cache.find("A").orElse(new A())
B b = cache.find("B").orElse(new B())
作为一种解决方法,我有一个静态实用方法如下:
public static <S, T extends S> Optional<S> convertOptional(Optional<T> optional) {
return (Optional<S>)optional;
}
所以我的最后一个问题是,这是否与上面的“理想”代码类型安全?
A a = OptionalUtil.<A,B>convertOptional(cache.find("A")).orElse(new A());
答案 0 :(得分:5)
您有效地尝试查看Optional<B>
作为Optional<A>
返回而不更改返回类型(因为您无法使用super
)。我只会map
identity function。
A a = cache.find("A").map(Function.<A> identity()).orElse(new A());
// or shorter
A a = cache.find("A").<A> map(x -> x).orElse(new A());
我认为你的方法没有任何问题。
答案 1 :(得分:2)
是的,您的代码是类型安全的,因为您将Optional<T>
强制转换为Optional<S>
,而T始终是S.您可以使用@SuppressWarnings("unchecked")
注释向编译器指明此代码你的convertOptional实用工具方法。
除了其他优秀的答案,你也可以这样做。请注意,与第一个版本相比,缺少泛型。
package com.company;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
public class Cache {
private static final Function<A,A> CAST_TO_A = Function.<A>identity();
private final Map<String, B> cache = new HashMap<>();
public Optional<B> find(String s) {
return Optional.ofNullable(cache.get(s));
}
public static void main(String[] args) {
Cache cache = new Cache();
Optional<B> potentialA = cache.find("A");
A a = potentialA.isPresent() ? potentialA.get() : new A();
A anotherWay = cache.find("A").map(CAST_TO_A).orElse(new A());
B b = cache.find("B").orElse(new B());
}
public static class A {}
public static class B extends A {}
}
答案 2 :(得分:1)
在实践中,我认为它是类型安全的,因为Optional
类是不可变的,T是S的子类型。
请注意,虽然T is subtype of S
NOT 表示Optional<T> is subtype of Optional<S>
,但从理论上讲,施法不正确。在某些情况下,进行此类演员表并不安全,并且在运行时可能会出现问题(与List
一样)。
所以我的建议是尽可能避免强制转换,我宁愿定义像getOrElse
这样的方法。此外,为了完整起见,方法convertOptional
中的泛型类型可以简化如下。
class OptionalUtil {
public static <S> S getOrElse(Optional<? extends S> optional, Supplier<? extends S> other) {
return optional.map(Function.<S>identity()).orElseGet(other);
}
public static <S> Optional<S> convertOptional(Optional<? extends S> optional) {
return (Optional<S>)optional;
}
}
他们可以这样使用:
A a1 = OptionalUtil.getOrElse(cache.find("A"), A::new);
A a2 = OptionalUtil.<A>convertOptional(cache.find("A")).orElseGet(A::new);
[编辑] 我将方法orElse
替换为orElseGet
,因为前者会创建一个新的A
对象,即使{{1}存在。