【问题标题】:How can a concise closure be written for a generic method?如何为泛型方法编写简洁的闭包?
【发布时间】:2018-10-04 10:10:54
【问题描述】:

我想编写一个具有泛型方法的功能性非泛型接口的实现。实现需要是内联闭包且简洁。

作为一个简化的示例

@FunctionalInterface interface Fn {
    <R> R fn(R arg);
}
public class Scratch {
    Fn id = arg -> arg;
    //Fn nul = arg -> null;
    //Fn requiresNonNull = ...
}

给了

/Scratch.java:5: error: incompatible types: invalid functional descriptor for lambda expression
    Fn id = arg -> arg;
            ^
    method <R>(R)R in interface Fn is generic
  where R is a type-variable:
    R extends Object declared in method <R>fn(R)
1 error

(实际上,参数将是一个泛型接口,其方法的返回类型为R。)

是否有一种解决方法,而不用回到冗长的匿名内部类?

有一个明显相似的问题,“Cannot convert functional interface with generic method into lambda expression”,但这源于使用名为 Integer 的类型参数,而不是像 T 这样的传统参数,Jon Skeet 接受的回答说他不知道解决方案我的问题。

还有一个很长的讨论“Functional interface confusion”未能回答这个问题。不可能是“这里最好是冗长的匿名内部类”,对吗?

【问题讨论】:

  • :P 如果 Jon Skeet 不知道...

标签: java lambda generic-method generic-lambda


【解决方案1】:

泛型 lambda 是不合法的,但泛型方法引用是合法的。你可以通过创建辅助方法来减少匿名类的冗长:

public class Scratch {
    Fn id = Scratch::id;
    Fn nul = Scratch::nul;
    Fn requiresNotNull = Objects::requireNonNull;

    private static <R> R id(R arg) {
        return arg;
    }

    private static <R> R nul(R arg) {
        return null;
    }
}

【讨论】:

  • 这是引用讨论中的解决方案。闭包需要内联(并且是闭包)。只是在不明显的简单情况下。也许我应该添加一个更现实但更复杂的例子。
  • (不是我的反对意见,顺便说一句。我很高兴你为我的问题写了一个答案。)
【解决方案2】:

经过大量的实验和间接,我有一个解决方案。我在命名方面不太成功。

这就是想法

  • 功能接口和单个抽象方法都没有类型参数。
  • 功能接口接收带有类型参数但在方法参数中使用通配符的使用者。
  • 消费者只是一个内部gubbins,但它确实有那个类型参数。它用于在执行通过封闭函数返回时存储结果。
  • 消费者本身会收到一个功能接口,其中包含实际业务功能实例,该实例属于参数化类型。
  • 有一种默认方法将事物联系在一起,包括创建消费者。

清楚吗? [修辞]

所以不是会写

Fn id = arg -> arg;

我们至少可以写

Fn id = q -> q.q(arg -> arg);

这是一个 lambda lambda 工厂。

我们好像语法用完了,写不出类似的东西

Fn id = Fn.Consumer::q(arg -> arg); // not valid syntax!

总之(有一个主要表明我没有作弊)

import java.util.concurrent.atomic.*;

@FunctionalInterface interface Fn {
    interface Instance<R> {
        R fn(R arg);
    }
    interface Consumer<R> {
       void q(Instance<R> gn);
    }

    void consume(Consumer<?> consumer);

    default <R> R fn(R arg) {
        AtomicReference<R> result = new AtomicReference<>();
        this.consume((Instance<R> instance) -> { result.set(instance.fn(arg)); });
        return result.get();
    }
}

public interface Scratch {
    Fn id = q -> q.q(arg -> arg);
    Fn nul = q -> q.q(arg -> null);

    public static void main(String[] args) {
        String idStr = id.fn("cruel");
        String nulStr = nul.fn("cruel");

        System.err.println(idStr);
        System.err.println(nulStr);
    }
}

我不认为我利用了类型系统中的任何缺陷。

(我可能应该在问题中添加一个更复杂的示例来说明您为什么要这样做。)

【讨论】:

  • 哇...我的头好痛。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-09
  • 2010-12-06
  • 2014-10-02
  • 2014-01-26
相关资源
最近更新 更多