Поэтому я размышлял над усовершенствованием, необходимым для проекта Tyrus, которое позволило бы пользователю транслировать данные на подмножество клиентов, подключенных к URL-адресу на кластере машин. Есть разные способы сделать это; но так как я играл с JDK 8, эта проблема определенно выглядела как гвоздь.
С этой целью я создал простой класс модульного тестирования, который будет брать мой фильтр, сериализовать его на диск, читать обратно и затем выполнять. У него было поле экземпляра «VALUE», которое мы могли использовать для прямой или косвенной ссылки, чтобы выяснить, что может вызвать сбой сериализации.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.NotSerializableException;import java.io.ObjectInput;import java.io.ObjectInputStream;import java.io.ObjectOutput;import java.io.ObjectOutputStream;import java.io.Serializable;import java.util.function.Predicate;import org.junit.Test;public class SerializablePredicateFilterTest { public String VALUE = "Bob"; public interface SerializablePredicate<T> extends Predicate<T>, Serializable { } public <T> void filter(SerializablePredicate<T> sp, T value) throws IOException, ClassNotFoundException { sp.getClass().isLocalClass(); File tempFile = File.createTempFile("labmda", "set"); try (ObjectOutput oo = new ObjectOutputStream(new FileOutputStream(tempFile))) { oo.writeObject(sp); } try (ObjectInput oi = new ObjectInputStream(new FileInputStream(tempFile))) { SerializablePredicate<T> p = (SerializablePredicate<T>) oi.readObject(); System.out.println(p.test(value)); } }} |
Так что просто для калибровки давайте удостоверимся, что анонимный внутренний класс потерпит неудачу, потому что он всегда будет содержать ссылку на включающий объект….
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
@Test(expected = NotSerializableException.class) public void testAnonymousDirect() throws IOException, ClassNotFoundException { String value = VALUE; filter(new SerializablePredicate<String>() { @Override public boolean test(String t) { return value.length() > t.length(); } }, "Bob"); } |
То же самое верно для локальных классов, что вы не используете локальные классы?
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
@Test(expected = NotSerializableException.class) public void testLocalClass() throws IOException, ClassNotFoundException { class LocalPredicate implements SerializablePredicate<String> { @Override public boolean test(String t) { // TODO Implement this method return false; } } filter(new LocalPredicate(), "Bobby"); } |
Таким образом, автономный класс, конечно, будет работать, в данном случае для удобства — вложенный.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
public static class LengthPredicate implements SerializablePredicate<String> { private String value; public LengthPredicate(String value) { super(); this.value = value; } public void setValue(String value) { this.value = value; } public String getValue() { return value; } @Override public boolean test(String t) { // TODO Implement this method return false; } } @Test public void testStaticInnerClass() throws IOException, ClassNotFoundException { filter(new LengthPredicate(VALUE), "Bobby"); } |
Итак, давайте вернемся к JDK 8, оказалось, что моя первая попытка также не удалась, но это подтверждает, что сериализация вполне устраивает Lambda в целом.
|
1
2
3
4
5
6
|
@Test(expected = NotSerializableException.class) public void testLambdaDirect() throws IOException, ClassNotFoundException { filter((String s) -> VALUE.length() > s.length(), "Bobby"); } |
Небольшая модификация для копирования значения в эффективно конечные атрибуты, и вот лямбда теперь сериализуется и извлекается правильно.
|
1
2
3
4
5
6
7
8
|
@Test public void testLambdaInDirect() throws IOException, ClassNotFoundException { String value = VALUE; filter((String s) -> value.length() > s.length(), "Bobby"); } |
И, конечно, если значение является параметром простого метода, оно также работает нормально.
|
01
02
03
04
05
06
07
08
09
10
|
@Test public void testLambdaParameter() throws IOException, ClassNotFoundException { invokeWithParameter(VALUE); } private void invokeWithParameter(String value) throws java.lang.ClassNotFoundException, java.io.IOException { filter((String s) -> value.length() > s.length(), "Bobby"); } |
Так что ответ — да, вы можете заставить его сериализоваться, если вы немного осторожны.