我有一个
的界面public static interface MyClass{
public boolean doSomething(boolean a, boolean b);
}
然后我实例化一个变量
MyClass a = (boolean x, boolean y) -> x && y;
现在我的问题是,如果我想获取参数x和y以及返回值,我将如何去做?我想获取参数的原因是因为我想序列化,以便我可以用它们ObjectOutputStream.writeObject()
。
不确定我的问题是否完全有意义。我没有太多地使用lambda,所以任何形式的指导都会受到高度赞赏。
添加:好的,所以MyClass a在另一个类中实例化,让我们称之为ParentClass
。所以现在我有了
MyClass a = (x, y) -> x && y;
ParentClass b = new ParentClass();
b.setMyClass(a);
现在,在我正在上课的主要班级中,我通过了ParentClass b
。所以我接受并做到了
MyClass c = b.getMyClass;
如果我理解正确的话,那就是对(x, y) -> x && y;
的引用。现在的问题是,如何获取参数值和返回值,以便我可以序列化它。 MyClass也不总是(x,y) -> x && y
,它可能是任何东西,即
MyClass a = (x, y) -> x || y;
MyClass a = (x, y) -> x^y;
MyClass a = (x, y) -> true;
我不允许修改MyClass或ParentClass。这就是为什么我试图获取传递的参数和返回值,以便我可以序列化并发送它们。
答案 0 :(得分:3)
我怀疑你误解了lambda表达式是如何工作的。您的问题意味着您希望创建a
对象,然后对其进行序列化,并在此过程中以某种方式记录x
和y
的值。我理解正确吗?
如果是这样,那么从根本上讲,这不是lambdas的工作方式。它们不是普通意义上的包含状态的对象。相反,可以将它们视为可以传递的一些可执行代码,可以随时应用于一组值(我怀疑我会在评论中因为过于简单的解释而受到重创!)。
您可以序列化lambda,但在这种情况下,您将序列化由lambda表示的操作,而不是在任何给定调用中传递给它的值。
如果您希望MyClass
接口的实现可序列化,则需要扩展Serializable
。它应该如下所示:
@FunctionalInterface
public interface MyClass extends Serializable {
boolean doSomething(boolean a, boolean b);
}
如果您无法修改MyClass
,那么您仍然可以将MyClass
对象转换为Serializable
:
Serializable a = (Serializable & MyClass)(a, b) -> a || b;
或者如果您使用ParentClass
:
Serializable a = (Serializable)b.getMyClass();
然后您应该能够序列化a
。
请记住,当您反序列化时,需要转回MyClass
以便可以使用它。它看起来像:
MyClass b = (Serializable & MyClass)objectInStream.readObject();
b.doSomething(true, false);
答案 1 :(得分:2)
在Java中,简单地说,lambda语法是一种描述方法行为而不给它命名的方法。
e.g。 (Type1 arg1, Type2 arg2, ...) -> <expression>
定义一个看起来像这样的函数:
public TypeOfExpression anonymousMethod(Type1 arg1, Type2 arg2, ...) {
...
return <expression>;
}
查看Java Tutorials以获取有关lambdas的更多信息。
实际上lambda表达式(boolean x, boolean y) -> x && y
(当你定义它时给定类型MyClass
)只不过是以下的语法糖:
new MyClass() {
public boolean doSomething(boolean x, boolean y) {
return x && y;
}
}
希望能够清楚地说明lambdas在Java中代表什么。
在技术术语中,您提供的界面定义定义了'function type'。在Java中,任何只有一个方法的接口都称为“functional”,并且可以作为与接口中单个方法相同类型的lambda表达式的类型而不是接口使用。
这个决定可能是因为如果一个接口只有一个方法,那么指定它的所有方法就是该方法的行为,它可以用lambda表示法更简洁地写出来,如上所示。
因此,lambda表达式只是接口的一个实例 - 特别是它没有字段,但定义了一个方法行为。我认为你假设它封装了一对输入及其输出结果。如果这是您所需要的,那么您可能正在寻找更像内置操作的对类型(在本例中为&&
)。
编辑:关于可串行化。
很明显,您需要序列化MyClass类型的未知函数。如果MyClass
扩展Serializable
,您应该能够适当地进行投射和序列化。如果您无法控制MyClass
,因此无法确保这一点,以下特别观察可能有所帮助。
如果MyClass
中的方法确实属于此处显示的类型(boolean, boolean) -> boolean
,则可以考虑手动序列化的案例数量足够少。
类型A -> B
的“可能”(总)函数的数量,对于有限大小的类型A
和B
,|B|
^ |A|
( B
的大小与A
的功率大小相同。这是因为,计算可能的函数,对于A
类型的每个输入,函数可以提供|B|
个可能的输出。
此外,对(A, B)
类型的大小为|A|
* |B|
。
在我们的案例中,我们有(boolean, boolean) -> boolean
。但boolean
只有2号!因此(boolean, boolean)
的大小为4,(boolean, boolean) -> boolean
的大小为2 ^ 4 = 16。
我们如何计算出我们手中的这16项功能中的哪一项?只需枚举可能的输入(只做4个)。然后我们可以在这样的变量中记录四个输出(其中a
将是我们未知的函数。)
boolean ttout = a(true, true);
boolean tfout = a(true, false);
boolean ftout = a(false, true);
boolean ffout = a(false, false);
然后我们可以快乐地序列化这四个值。 :)
此外,如果我们反序列化并获得四个值(ttout
,tfout
...),我们可以重建这样的函数:
a = (boolean x, boolean y) -> {
if (x) {
if (y) return ttout;
else return tfout;
} else {
if (y) return ftout;
else return ffout;
}
}
答案 2 :(得分:1)
如果您希望a
本身可序列化,则可以编写a = (MyClass & Serializable) (boolean x, boolean y) -> x && y
。
仅x
的定义中没有y
或a
:这些仅在实际调用a
时存在。