有什么办法规避lambda表达式的类型吗? [英] Is there any way to circumvent the typedness of lambda expressions?

查看:239
本文介绍了有什么办法规避lambda表达式的类型吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个我想知道的问题,因为lambdas是在Java中引入的,并且受到一个相关问题,我认为我可以把它放在这里,看看是否有任何想法。



(附注:有一个类型该变量是固定的 - 这正是我试图规避)






通过目标类型推断,它们需要的类型。这都是由编译器处理。例如,函数

  static void useF(Function< Integer,Boolean> f){...} 
static void useP(Predicate< Integer> p){...}

strong>相同 lambda表达式:

  useF(x  - > true); 
useP(x - > true);

表达式将一次显示为实现 Function< Integer的类,布尔> 接口,并且一次作为实现 Predicate< Integer> 接口的类。



但是不幸的是,没有办法存储适用于两个函数的类型的lambda表达式, GenericLambdaType lambda = x - > true;



这种通用lambda类型必须对可以实现的方法的类型进行编码由给定的lambda表达式。所以在这种情况下,它将是



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

/guides/jni/spec/types.html#type_signatures\">标准类型签名
,以供说明)。 (这不是完全不合理: C ++ lambda表达式基本上正是这样做...)






那么是否有任何方法可以防止lambda表达式被解析为某个特定类型?



特别是,有没有任何技巧或解决方法允许 useF useP 使用相同的对象调用上面的方法,如

  useF 
useP(theObject);

这不太可能,所以我认为答案显然是:不,但:

$

  

useF(convertToRequiredTargetType(theObject));
useP(convertToRequiredTargetType(theObject));






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



似乎没有简单的解决方法。通过将表达式包装成通用辅助方法(如

  static< T> T provide()
{
return x - >真正;
}

当然失败,说明此表达式的目标类型必须是一个功能接口(类型可以简单地不在这里推断)。但我也考虑过其他选项,如 MethodHandles ,残酷未选中铸造或讨厌的反射黑客。一切似乎在编译之后立即丢失,其中lambda隐藏在匿名类的匿名对象中,其唯一的方法通过 InvokeVirtual ...

解决方案

假设在一个闪亮的未来(例如Java 10或11),我们有真正的函数类型,允许指定一个函数,特定常规Java类型(并且是某种类型的值而不是对象等)。然后我们还有问题,现有的方法

  static void useF(Function< Integer,Boolean> f){... } 
static void useP(Predicate< Integer> p){...}



实现传统的Java 接口并且表现得像Java对象那样的Java对象,即不会突然改变 theObject instanceof Function theObject instanceof Predicate 。这意味着它不会是通用函数突然开始实现所需的接口,当传递到这些方法中的任何一个,而是某种捕获转换适用,产生实现所需的目标接口的对象,很像今天,当你将lambda表达式传递到这些方法中的任何一个或将 Predicate 转换为函数使用 p :: test (或使用 f :: apply )反之亦然。



所以不会发生的是,你传递相同的对象到这两个方法。你只有一个隐式转换,它将在编译时确定,并可能像在今天的lambda表达式一样在字节码中显式。






一个类似 convertToRequiredTargetType 的方法不能工作,因为它不知道目标类型。使这样的事情工作的唯一解决方案是你已经排除,预编译器和字节码操作。您可以创建一个接受附加参数的方法,描述require 接口对象, c $ c> LambdaMetaFactory 但是该方法必须重做编译器所做的一切,确定函数签名,实现方法的名称等。



对于没有好处,调用像 convertToRequiredTargetType(theObject)(或实际上 convertToRequiredTargetType(theObject,Function.class) code>)绝不比例如简单 theObject :: test )。你希望创建这样一个方法引起了你的奇怪的语句一切似乎在编译后立即丢失,其中lambda隐藏在一个匿名类的匿名对象实际上,你有一个对象实现一个已知的功能接口签名,因此可以转换为 function :: methodName (其中IDE可以为您完成方法名称,如果您已经忘记)... ...


This is a question that I wondered about since the lambdas had been introduced in Java, and inspired by a related question, I thought that I might bring it up here, to see whether there are any ideas.

(Side notes: There is a similar question for C#, but I did not find one for Java. The questions for Java about "storing a lambda in a variable" always referred to cases where the type of the variable was fixed - this is exactly what I'm trying to circumvent)


Lambda expressions receive the type that they need, via target type inference. This is all handled by the compiler. For example, the functions

static void useF(Function<Integer, Boolean> f) { ... }
static void useP(Predicate<Integer> p) { ... }

can both be called with the same lambda expression:

useF(x -> true);
useP(x -> true);

The expression will once manifest itself as a class implementing the Function<Integer,Boolean> interface, and once as a class implementing the Predicate<Integer> interface.

But unfortunately, there is no way of storing the lambda expression with a type that is applicable to both functions, like in

GenericLambdaTypelambda = x -> true;

This "generic lambda type" would have to encode the type of the method that can be implemented by the given lambda expression. So in this case, it would be

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

(based on the standard type signatures, for illustration). (This is not completely unreasonable: The C++ lambda expressions are basically doing exactly that...)


So is there any way to prevent a lambda expression being resolved to one particular type?

Particularly, is there any trick or workaround that allows the useF and useP methods sketched above to be called with the same object, as in

useF(theObject);
useP(theObject);

This is unlikely, so I assume the answer will plainly be: "No", but: Could there be any way to write a generic, magic adaption method like

useF(convertToRequiredTargetType(theObject));
useP(convertToRequiredTargetType(theObject));

?


Note that this question is more out of curiosity. So I'm literally looking for any way to achieve this (except for custom precompilers or bytecode manipulation).

There seem to be no simple workarounds. A naive attempt to defer the type inference, by wrapping the expression into a generic helper method, as in

static <T> T provide()
{
    return x -> true;
}

of course fails, stating that "The target type of this expression must be a functional interface" (the type can simply not be inferred here). But I also considered other options, like MethodHandles, brutal unchecked casts or nasty reflection hacks. Everything seems to be lost immediately after the compilation, where the lambda is hidden in an anonymous object of an anonymous class, whose only method is called via InvokeVirtual...

解决方案

Suppose in a shiny future (say Java 10 or 11) we have true function types which allow to specify a function without forcing it to be of a particular conventional Java type (and being some kind of value rather than an object, etc.). Then we still have the issue that the existing methods

static void useF(Function<Integer, Boolean> f) { ... }
static void useP(Predicate<Integer> p) { ... }

expect a Java object implementing a conventional Java interface and behaving like Java objects do, i.e. not suddenly changing the result of theObject instanceof Function or theObject instanceof Predicate. This implies that it will not be the generic function that suddenly starts implementing the required interface when being passed to either of these methods but rather that some kind of capture conversion applies, producing an object implementing the required target interface, much like today, when you pass a lambda expression to either of these methods or when you convert a Predicate to a Function using p::test (or vice versa using f::apply).

So what won’t happen is that you are passing the same object to both methods. You only have an implicit conversion, which will determined at compile-time and likely made explicit in byte code just as with today’s lambda expressions.


A generic method like convertToRequiredTargetType can’t work because it has no knowledge about the target type. The only solutions to make such a thing work are the ones you have precluded, precompilers and byte code manipulation. You could create a method accepting an additional parameter, a Class object describing the require interface, which delegates to the LambdaMetaFactory but that method would have to redo everything the compiler does, determining the functional signature, the name of the method to implement, etc.

For no benefit, as invoking that utility method like convertToRequiredTargetType(theObject) (or actually convertToRequiredTargetType(theObject, Function.class)) is in no way simpler than, e.g. theObject::test). Your desire to create such a method caused your weird statement "Everything seems to be lost immediately after the compilation, where the lambda is hidden in an anonymous object of an anonymous class" when actually, you have an object implementing a functional interface with a known signature and therefore can be converted as simple as function::methodName (where the IDE can complete the method name for you, if you have forgotten)…

这篇关于有什么办法规避lambda表达式的类型吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆