Java 在不丢失通用性的情况下包装逆变函数接口
我有一些具有这种一般结构的代码:Java 在不丢失通用性的情况下包装逆变函数接口,java,generics,nested-generics,Java,Generics,Nested Generics,我有一些具有这种一般结构的代码: interface Func<A> { double apply(Optional<A> a); } class Foo { public double compute(Func<Double> f) { // Sometimes amend the function to do something slightly different Func<Double> g
interface Func<A> {
double apply(Optional<A> a);
}
class Foo {
public double compute(Func<Double> f) {
// Sometimes amend the function to do something slightly different
Func<Double> g = f;
if (someCondition())
g = oa -> Math.max(0, f.apply(oa));
return g.apply(Optional.of(3.14)) + g.apply(Optional.empty());
}
}
不幸的是,现在lambda没有进行类型检查(称为Eclipse),因为oa
的类型不能传递给f.apply
将f
本身赋值给g
似乎不会让编译器担心,如果apply
的参数是A
而不是可选的
,则不会出现问题
不幸的是,似乎没有办法命名?超级双参数
参数类型,因此我可以再次将其用作g
的类型和/或使用老式的内部类替换lambda
例如,这在语法上是不允许的:
public <T super Double> double compute(Func<T> f) {
Func<T> g = f;
...
公共双计算(Func f){
Func g=f;
...
有什么合理优雅的方法来完成这项工作吗?
到目前为止,我想到的最好的办法是
public double compute(Func<? super Double> f) {
if (someCondition())
return computeInner(oa -> Math.max(0, f.apply(oa)));
else
return computeInner(f);
}
private double computeInner(Func<? super Double> g) {
return g.apply(Optional.of(3.14)) + g.apply(Optional.empty());
}
public double compute(Func试试这个奇怪的把戏:
g = oa -> Math.max(0, f.apply(oa.map(a -> a)));
// ^----------^
这样映射可选的类型允许编译器将可选的类型“强制转换”为一致的类型
这样做的缺点是创建一个新的可选
实例
但是,当然,它会考虑这到底是规范允许的,还是一个bug
就我个人而言,我并不认为你的“迄今为止最好的”特别令人震惊。当然,这取决于实际代码的外观。一般来说,我觉得Java中的超边界类型推断是一场噩梦。有许多编译器错误,通常很难解释为什么编译器会拒绝某些语法而不是其他语法
这就是说,你可以通过将f
强制转换为Func
类型来解决这个问题。这并不是一个完全安全的强制转换;如果f
有任何状态,我们可以假设f
的下限是双而实际上它可能是Number
或Object
。对于强制转换,你不需要不必承担@AndyTurner提供的解决方案的性能损失,但未经检查的强制转换也不是您的朋友
public double compute(Func<? super Double> f) {
// Sometimes amend the function to do something slightly different
Func<? super Double> g = f;
if (someCondition())
g = oa -> Math.max(0, (((Func<Double>) f)).apply(oa));
return g.apply(Optional.of(3.14)) + g.apply(Optional.empty());
}
public double compute(Func解决方案是更改您的接口签名:
double apply(Optional<? extends A> a);
这样错误就永远不会发生
这是因为Double
可分配给Object
。编译器将自动调整类型。这意味着接口实际上是:
double apply(? extends A a);
因此,您需要做的是让您的界面具有这种适应能力
func(Number)
可以接受Double
作为参数
func(可选)
也应接受Optional
因此,您应该在您的界面上添加?extends
。这与您的问题无关,但是如果您使用java.util.function.function而不是您的,您可以执行f,然后(a->Math.max(0,a))
@killjoy:我首先尝试将示例最小化为函数
,但只有当接口本身在参数类型中提供了可选
包装时,问题才会出现。(我正在查看的实际代码中有一组与该问题无关的函数附加参数)。您可以使用Number.doubleValue()
而不是正常取消装箱。@killjoy:如果有人想通过Func
,这对他们没有帮助。啊,我以前不得不使用这种破解;但这只可能是因为可选的
提供了API。对于没有可用转换API的情况,我想知道是否有更通用的模式可以遵循;)
double apply(A a);
double apply(? extends A a);