Skip to content

Commit

Permalink
Add serializer for java 8's java.util.Optional
Browse files Browse the repository at this point in the history
It's registered as default serializer if java.util.Optional is
available in the classpath. This is preferred to checking the java
version (from sys props like e.g. "java.specification.version")
because some platforms might provide Optional while not supporting
the java 8 spec - who knows.
  • Loading branch information
magro committed Feb 27, 2016
1 parent 63231a3 commit 12229ca
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 2 deletions.
4 changes: 3 additions & 1 deletion src/com/esotericsoftware/kryo/Kryo.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import java.util.TreeMap;
import java.util.TreeSet;

import com.esotericsoftware.kryo.serializers.java8.OptionalSerializer;
import org.objenesis.instantiator.ObjectInstantiator;
import org.objenesis.strategy.InstantiatorStrategy;
import org.objenesis.strategy.SerializingInstantiatorStrategy;
Expand Down Expand Up @@ -115,7 +116,7 @@ public class Kryo {
static private final int NO_REF = -2;

private SerializerFactory defaultSerializer = new ReflectionSerializerFactory(FieldSerializer.class);
private final ArrayList<DefaultSerializerEntry> defaultSerializers = new ArrayList(32);
private final ArrayList<DefaultSerializerEntry> defaultSerializers = new ArrayList(33);
private final int lowPriorityDefaultSerializerCount;

private final ClassResolver classResolver;
Expand Down Expand Up @@ -210,6 +211,7 @@ public Kryo (ClassResolver classResolver, ReferenceResolver referenceResolver, S
addDefaultSerializer(TimeZone.class, TimeZoneSerializer.class);
addDefaultSerializer(Calendar.class, CalendarSerializer.class);
addDefaultSerializer(Locale.class, LocaleSerializer.class);
OptionalSerializer.addDefaultSerializer(this);
lowPriorityDefaultSerializerCount = defaultSerializers.size();

// Primitives and string. Primitive wrappers automatically use the same registration as primitives.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.esotericsoftware.kryo.serializers.java8;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;

import java.util.Optional;

import static com.esotericsoftware.minlog.Log.DEBUG;
import static com.esotericsoftware.minlog.Log.debug;

/**
* Serializer for {@link Optional}.
*/
public class OptionalSerializer extends Serializer<Optional> {

private static final boolean OPTIONAL_SUPPORTED;

static {
boolean supported = false;
try {
Class.forName("java.util.Optional");
supported = true;
} catch (Exception e) {
if (DEBUG) {
debug("Class 'java.util.Optional' not found, 'OptionalSerializer' won't be registered as default serializer.");
}
}
OPTIONAL_SUPPORTED = supported;
}

{
setAcceptsNull(false);
}

@Override
@SuppressWarnings("unchecked")
public void write(Kryo kryo, Output output, Optional object) {
Object nullable = object.isPresent() ? object.get() : null;
kryo.writeClassAndObject(output, nullable);
}

@Override
public Optional read(Kryo kryo, Input input, Class<Optional> type) {
return Optional.ofNullable(kryo.readClassAndObject(input));
}

@Override
public Optional copy(Kryo kryo, Optional original) {
if(original.isPresent()) {
return Optional.of(kryo.copy(original.get()));
}
return original;
}

public static void addDefaultSerializer(Kryo kryo) {
if(OPTIONAL_SUPPORTED)
kryo.addDefaultSerializer(Optional.class, OptionalSerializer.class);
}
}
4 changes: 3 additions & 1 deletion test/com/esotericsoftware/kryo/SerializationCompatTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@
public class SerializationCompatTest extends KryoTestCase {

private static final String ENDIANNESS = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN ? "le" : "be";
private static final int JAVA_VERSION = Integer.parseInt(System.getProperty("java.version").split("\\.")[1]);
private static final int EXPECTED_DEFAULT_SERIALIZER_COUNT = JAVA_VERSION < 8 ? 33 : 34;

@Override
protected void setUp() throws Exception {
Expand All @@ -94,7 +96,7 @@ public void testDefaultSerializers() throws Exception {
" type of the new default serializer.\n" +
"After that's done, you must create new versions of 'test/resources/data*'" +
" because the new TestData instance will no longer be equals the formerly written/serialized one.",
33, defaultSerializers.size());
EXPECTED_DEFAULT_SERIALIZER_COUNT, defaultSerializers.size());
}

public void testStandard () throws Exception {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.esotericsoftware.kryo.serializers.java8;

import com.esotericsoftware.kryo.KryoTestCase;

import java.util.Objects;
import java.util.Optional;

public class OptionalSerializerTest extends KryoTestCase {

{
supportsCopy = true;
}

@Override
protected void setUp() throws Exception {
super.setUp();
kryo.register(Optional.class);
kryo.register(TestClass.class);
}

public void testNull() {
roundTrip(2, 2, new TestClass(null));
}

public void testEmpty() {
roundTrip(3, 3, new TestClass(Optional.<String>empty()));
}

public void testPresent() {
roundTrip(6, 6, new TestClass(Optional.of("foo")));
}

static class TestClass {
Optional<String> maybe;
public TestClass() {}
public TestClass(Optional<String> maybe) {
this.maybe = maybe;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TestClass testClass = (TestClass) o;
return Objects.equals(maybe, testClass.maybe);

}

@Override
public int hashCode() {
return Objects.hashCode(maybe);
}
}

}

0 comments on commit 12229ca

Please sign in to comment.