尽管可以在Java 8中序列化lambda,但强烈建议不要这样做.甚至不鼓励对内部类进行序列化.给出的原因是lambdas可能不会在另一个JRE上正确地反序列化.然而,这并不意味着这有是一个办法可以安全的序列化拉姆达?
例如,假设我将类定义为:
public class MyClass {
private String value;
private Predicate<String> validateValue;
public MyClass(String value, Predicate<String> validate) {
this.value = value;
this.validateValue = validate;
}
public void setValue(String value) {
if (!validateValue(value)) throw new IllegalArgumentException();
this.value = value;
}
public void setValidation(Predicate<String> validate) {
this.validateValue = validate;
}
}
Run Code Online (Sandbox Code Playgroud)
如果我像这样声明了类的实例,我不应该序列化它:
MyClass obj = new MyClass("some value", (s) -> !s.isEmpty());
Run Code Online (Sandbox Code Playgroud)
但是如果我像这样创建了一个类的实例呢:
// Could even be a static nested class
public class IsNonEmpty …Run Code Online (Sandbox Code Playgroud) 刚刚介绍Streams和Java 8 Lambda功能,以及对其他不言自明的Oracle doc Lambda Expressions的最后评论:
如果lambda表达式的目标类型及其捕获的参数是可序列化的,则可以序列化它.但是,与内部类一样,强烈建议不要对lambda表达式进行序列化.
检查这一点我发现了SO问题
OP处理来自客户端代码的序列化lambda表达式.
如果我有一个互联网服务和参数之一是一个lambda表达式,现在看来,这可能包含恶意代码,可以做这样的事情的文件系统访问权限,或导致堆栈溢出 - 所以这将是非常愚蠢的信任它.
我是否过度夸大了安全风险,或者序列化表达式可以包含什么限制?
请考虑以下使用交叉类型的能力的代码,它是在Java 8中添加的:
private <E, T extends List<E> & RandomAccess> void takeList(T list) {
}
private void fakingRandomAccess() {
List<Integer> linkedList = new LinkedList<>();
takeList((List<Integer> & RandomAccess)linkedList);
}
Run Code Online (Sandbox Code Playgroud)
我已经制定了一个takeList方法,它只采用具有(接近)恒定访问时间的列表,无论出于何种原因,但我可以想象有些情况确实需要这样做.
路过的ArrayList<E>进入方法应该工作得很好,但是通过交叉型也可以传递一个LinkedList<E>假装它有固定的访问时间.
现在我的问题是:
(List<Integer> & RandomAccess)linkedList在指定类型的对象中?takeList方法吗?考虑以下代码 -
public class TestCompletableFuture {
BiConsumer<Integer, Throwable> biConsumer = (x,y) -> {
System.out.println(x);
System.out.println(y);
};
public static void main(String args[]) {
TestCompletableFuture testF = new TestCompletableFuture();
testF.start();
}
public void start() {
Supplier<Integer> numberSupplier = new Supplier<Integer>() {
@Override
public Integer get() {
return SupplyNumbers.sendNumbers();
}
};
CompletableFuture<Integer> testFuture = CompletableFuture.supplyAsync(numberSupplier).whenComplete(biConsumer);
}
}
class SupplyNumbers {
public static Integer sendNumbers(){
return 25; // just for working sake its not correct.
}
}
Run Code Online (Sandbox Code Playgroud)
以上的事情很好.但是sendNumbers也可以在我的情况下抛出一个检查过的异常,例如:
class SupplyNumbers {
public …Run Code Online (Sandbox Code Playgroud) 在我们的Web应用程序项目中,我们使用Redis来管理会话.为了支持它,我们正在序列化将存储在会话中的任何对象.
例如,我们使用DTO来保存用于在屏幕上显示的bean数据.即使DTO内部有任何其他对象(组合),我们也必须序列化它,否则我们得到NotSerializableException.
当我创建一个匿名内部类来实现Comparator下面这样的时候,我遇到了一个问题:
Collections.sort(people, new Comparator<Person>() {
public int compare(Person p1, Person p2) {
return p1.getLastName().compareTo(p2.getLastName());
}
});
Run Code Online (Sandbox Code Playgroud)
上面的代码抛出了NotSerializableException,我通过创建一个实现Comparator以及Serializable接口的类来解决它.问题是,它被抛入JSP使用此DTO 的页面内.我不得不做很多调试才能找到实际问题.
但是现在,我想知道更改上面的代码以使用如下的Lambda表达式:
Collections.sort(people, (p1, p2) -> p1.getLastName().compareTo(p2.getLastName()));
Run Code Online (Sandbox Code Playgroud)
但是,我担心可能会发生同样的异常.Lambda表达式是否在内部创建对象?
我想知道对特定类型的任意对象的实例方法的引用是否可序列化?
例:
public class MyClass {
public void foo() {
System.out.println("Serializable");
}
}
Run Code Online (Sandbox Code Playgroud)
SerializableConsumer
@FunctionalInterface
public interface SerializableConsumer<T> extends Consumer<T>, Serializable {
}
Run Code Online (Sandbox Code Playgroud)
和领域是:
SerializableConsumer<MyClass> serializableMethod = MyClass::foo;
Run Code Online (Sandbox Code Playgroud)
EDITED
(实际上,这个问题与lambdas没有直接关系,但是对于使用边界的强制转换,所以标记为重复的问题并没有提供这个问题的答案.你会在这里找到我的问题的答案:我应该如何为Java转换具有多个边界的泛型?)
就在最近,我参加了SW工艺会议.在其中一个讨论的例子中,我遇到了一些这样的演员,它似乎是自Java 8以来的有效Java.
Object aTest = (String & CharSequence) "test";
Run Code Online (Sandbox Code Playgroud)
我的问题:有人可以告诉我这背后的成语以及它究竟能为我们做些什么吗?该示例的演示者无法解释它,我没有找到任何提及(我甚至不知道它的名称).
除了Oleksandr之外,我还要在这里提出另一个答案,因为这个问题已被标记为重复,因此被锁定.
与此同时,我能够构建一个(人为的)用例,只是为了明确这个想法.
假设我们有泛型方法foo(),它的唯一参数是通用的,而type参数有两个上限(反对Comparable和Serializable):
public static <T extends Comparable & Serializable> void foo(T t) {
System.out.println(t);
}
Run Code Online (Sandbox Code Playgroud)
进一步假设,我们有一个类AClass,实现Comparable和Serializable
public class AClass implements Comparable, Serializable {
@Override
public int compareTo(Object other) {
return 0;
}
}
Run Code Online (Sandbox Code Playgroud)
进一步假设设计的部分,我们有一个AClass对象instance,它被分配给一个类型的变量Object:
Object instance = new AClass();
Run Code Online (Sandbox Code Playgroud)
如果,在代码的另一个地方,我们想要传递instance给foo(),并且我们不知道动态类型 …
来自JDK8的Comparator.java:
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
Run Code Online (Sandbox Code Playgroud)
请注意,return语句带有一个有趣的强制转换前缀: (Comparator<T> & Serializable)
我已经意识到并且(我认为)理解:
&泛型类型限制的运算符(并且可以在此推断其用途),Serializable接口.然而,在演员阵容中一起使用对我来说是令人困惑的.
& Serializable如果返回类型不需要它的目的是什么?
我不明白这个意图.
对欺骗/关闭请求的后续行动:这个问题如何序列化lambda?不回答问题.我的问题特别注意到返回类型没有提及Serializable,因此我的困惑的来源.
我知道我们可以将函数转换为Serializable需要它的位置.
但是,我想将此转换移动到通用方法,以使使用代码更简洁.我无法创建这样的方法.
我的具体问题是下面的地图不是Serializable:
final Map<MyObject, String> map =
new TreeMap<>(Comparator.comparing(MyObject::getCode));
Run Code Online (Sandbox Code Playgroud)
我可以通过使用:
final Map<MyObject, String> map =
new TreeMap<>(Comparator.comparing((Function<MyObject, String> & Serializable) MyObject::getCode));
Run Code Online (Sandbox Code Playgroud)
但我希望能够做到这样的事情:
final Map<MyObject, String> map =
new TreeMap<>(Comparator.comparing(makeSerializable(MyObject::getCode)));
public static <T, U> Function<T, U> makeSerializable(Function<T, U> function) {
return (Function<T, U> & Serializable) function;
}
Run Code Online (Sandbox Code Playgroud)
对于编译器这很好,但在运行时,我得到一个ClassCastException:
java.lang.ClassCastException: SerializableTest$$Lambda$1/801197928 cannot be cast to java.io.Serializable
Run Code Online (Sandbox Code Playgroud)
我也尝试了以下替代方案,但没有成功:
// ClassCastException
public static <T extends Serializable, U extends Serializable> Function<T, U> makeSerializable(Function<T, U> …Run Code Online (Sandbox Code Playgroud) 我在这里寻找设计原理。
我当然可以理解使集合类可序列化,尽管 JCF 不这样做。然而,Procedure、IntProcedure等接口尤其是不存在的主要候选者Serializable,因为无论如何它们通常都是匿名的。
制作这些接口Serializable违背了 Josh Bloch 的建议,即接口应该很少扩展Serializable[1]。
我可能需要更新我的 Eclipse 首选项,以免为每个匿名 发出串行 uid 警告Procedure。
[1] 有效的 Java 第二版。第 291 页
我有一个带有lambda表达式的检票口项目。在用户单击后退按钮的一页上,我的应用程序崩溃:
java.lang.IllegalArgumentException: Invalid lambda deserialization
at x.y.z.MyPage$3.$deserializeLambda$(MyPage.java:1)
Run Code Online (Sandbox Code Playgroud)
在页面类(我返回的地方)中,我使用lambda表达式实现此接口:
public interface Localizator extends Serializable {
String getLocalizedString(String key);
}
Run Code Online (Sandbox Code Playgroud)
和lambda:
protected void someMethod() {
localize((String key) -> getString(key));
}
Run Code Online (Sandbox Code Playgroud)
当我将lambda更改为匿名类时,一切正常。在这种情况下应如何使用lambda?
信封:Java 1.8.0_25,Netbeans 8.0.2,Wicket 6.17.0。
编辑:这是带有lambda的真实(但简化)方法:
@Override
protected DataLoader createDataLoader() {
return new DataLoader(){
@Override
public List loadData() {
...
}
@Override
public List convertToTableRows(List data) {
return Converter.asRowList(
data,
(Record record) -> {...}, // this lambda is OK
(String key) -> getString(key)); // this lambda is crashing
}
@Override
public List …Run Code Online (Sandbox Code Playgroud) java ×9
java-8 ×8
lambda ×5
casting ×2
comparator ×1
exception ×1
generics ×1
redis ×1
serializable ×1
type-bounds ×1
wicket ×1