有没有办法规避lambda表达式的类型?

Mar*_*o13 9 java generics lambda casting

这是一个我想知道的问题,因为lambdas是用Java引入的,并且受到相关问题的启发,我想我可以在这里提出它,看看是否有任何想法.

(附注:C#有一个类似的问题,但是我没有找到一个用于Java的问题.关于"将lambda存储在变量中"的Java问题总是指变量类型被修复的情况 - 这正是我想要绕过的东西)


Lambda表达式通过目标类型推断接收它们所需的类型.这全都由编译器处理.例如,功能

static void useF(Function<Integer, Boolean> f) { ... }
static void useP(Predicate<Integer> p) { ... }
Run Code Online (Sandbox Code Playgroud)

可以使用相同的 lambda表达式调用它们:

useF(x -> true);
useP(x -> true);
Run Code Online (Sandbox Code Playgroud)

表达式将一度表现为实现Function<Integer,Boolean>接口的类,并且一次作为实现Predicate<Integer>接口的类.

但遗憾的是,没有办法将lambda表达式存储为适用于这两种函数的类型,例如

GenericLambdaTypelambda = x -> true;

这个"通用λ型"将具有以编码的方法的类型可以由给定的λ表达式来实现.所以在这种情况下,它会

(Ljava.lang.Integer)Ljava.lang.Booleanlambda = x -> true;

(基于标准类型签名,用于说明).(这不是完全不合理的:C++ lambda表达式基本上就是这样......)


那么有什么方法可以防止lambda表达式被解析为一种特定的类型?

特别是,是否有任何技巧或解决方法允许使用相同的对象调用上面描绘的useFuseP方法,如

useF(theObject);
useP(theObject);
Run Code Online (Sandbox Code Playgroud)

这是不太可能的,所以我假设答案显然是:"不",但是:有没有办法写一个通用的,魔术适应方法,如

useF(convertToRequiredTargetType(theObject));
useP(convertToRequiredTargetType(theObject));
Run Code Online (Sandbox Code Playgroud)


请注意,这个问题更多是出于好奇.所以我真的在寻找任何方法来实现这一点(除了自定义预编译器或字节码操作).

似乎没有简单的解决方法.通过将表达式包装到通用帮助器方法中来推迟类型推断的天真尝试,如

static <T> T provide()
{
    return x -> true;
}
Run Code Online (Sandbox Code Playgroud)

当然失败了,说" 这个表达式的目标类型必须是一个功能接口 "(这里不能推断出这种类型).但我也考虑过其他选择,比如MethodHandles,野蛮的未经检查的演员表或讨厌的反思黑客.编译后,所有内容似乎都会丢失,其中lambda隐藏在匿名类的匿名对象中,其唯一的方法是通过InvokeVirtual...

rge*_*man 3

我没有看到任何方法允许将解析为一种特定函数接口类型的 lambda 表达式直接解释为等效的函数接口类型。不存在两个功能接口都扩展或可以扩展的超级接口或“通用 lambda 类型”,即强制它仅采用一种特定类型的一个参数并返回一种特定类型。

但是您可以编写一个实用程序类,其中包含从一种类型的功能接口转换为另一种类型的方法。

该实用程序类将谓词转换为返回布尔值的函数,反之亦然。它包含身份转换,因此您不必担心是否调用转换方法。

public class Convert
{
    static <T> Predicate<T> toPred(Function<? super T, Boolean> func)
    {
        return func::apply;
    }

    static <T> Predicate<T> toPred(Predicate<? super T> pred)
    {
        return pred::test;
    }

    static <T> Function<T, Boolean> toFunc(Predicate<? super T> pred)
    {
        return pred::test;
    }

    static <T> Function<T, Boolean> toFunc(Function<? super T, Boolean> func)
    {
        return func::apply;
    }
}
Run Code Online (Sandbox Code Playgroud)

输入函数和谓词是 的使用者T,因此 PECS 规定? super。您还可以添加其他重载,这些重载可以采用BooleanSuppliers、或返回 a或 的Supplier<Boolean>任何其他函数接口类型。booleanBoolean

该测试代码可以编译。它允许您传递函数接口类型的变量并将其转换为所需的函数接口类型。如果您已经拥有确切的函数接口类型,则不必调用转换方法,但如果您愿意,也可以调用。

public class Main
{
    public static void main(String[] args)
    {
        Function<Integer, Boolean> func = x -> true;
        useF(func);
        useF(Convert.toFunc(func));
        useP(Convert.toPred(func));

        Predicate<Integer> pred = x -> true;
        useP(pred);
        useP(Convert.toPred(pred));
        useF(Convert.toFunc(pred));
    }

    static void useF(Function<Integer, Boolean> f) {
        System.out.println("function: " + f.apply(1));
    }
    static void useP(Predicate<Integer> p) {
        System.out.println("predicate: " + p.test(1));
    }
}
Run Code Online (Sandbox Code Playgroud)

输出是:

function: true
function: true
predicate: true
predicate: true
predicate: true
function: true
Run Code Online (Sandbox Code Playgroud)