Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/356.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在不丢失通用性的情况下包装逆变函数接口_Java_Generics_Nested Generics - Fatal编程技术网

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