Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace anonymous class creation with lambdas in ConstructorConstructor #2763

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
240 changes: 79 additions & 161 deletions gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,24 +103,14 @@ public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {
@SuppressWarnings("unchecked") // types must agree
InstanceCreator<T> typeCreator = (InstanceCreator<T>) instanceCreators.get(type);
if (typeCreator != null) {
return new ObjectConstructor<T>() {
@Override
public T construct() {
return typeCreator.createInstance(type);
}
};
return () -> typeCreator.createInstance(type);
}

// Next try raw type match for instance creators
@SuppressWarnings("unchecked") // types must agree
InstanceCreator<T> rawTypeCreator = (InstanceCreator<T>) instanceCreators.get(rawType);
if (rawTypeCreator != null) {
return new ObjectConstructor<T>() {
@Override
public T construct() {
return rawTypeCreator.createInstance(type);
}
};
return () -> rawTypeCreator.createInstance(type);
}

// First consider special constructors before checking for no-args constructors
Expand All @@ -147,11 +137,8 @@ public T construct() {
// of adjusting filter suggested below is irrelevant since it would not solve the problem
String exceptionMessage = checkInstantiable(rawType);
if (exceptionMessage != null) {
return new ObjectConstructor<T>() {
@Override
public T construct() {
throw new JsonIOException(exceptionMessage);
}
return () -> {
throw new JsonIOException(exceptionMessage);
};
}

Expand All @@ -167,11 +154,8 @@ public T construct() {
+ "; ReflectionAccessFilter does not permit using reflection or Unsafe. Register an"
+ " InstanceCreator or a TypeAdapter for this type or adjust the access filter to"
+ " allow using reflection.";
return new ObjectConstructor<T>() {
@Override
public T construct() {
throw new JsonIOException(message);
}
return () -> {
throw new JsonIOException(message);
};
}
}
Expand All @@ -183,42 +167,36 @@ public T construct() {
private static <T> ObjectConstructor<T> newSpecialCollectionConstructor(
Type type, Class<? super T> rawType) {
if (EnumSet.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() {
@Override
public T construct() {
if (type instanceof ParameterizedType) {
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
if (elementType instanceof Class) {
@SuppressWarnings({"unchecked", "rawtypes"})
T set = (T) EnumSet.noneOf((Class) elementType);
return set;
} else {
throw new JsonIOException("Invalid EnumSet type: " + type.toString());
}
return () -> {
if (type instanceof ParameterizedType) {
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
if (elementType instanceof Class) {
@SuppressWarnings({"unchecked", "rawtypes"})
T set = (T) EnumSet.noneOf((Class) elementType);
return set;
} else {
throw new JsonIOException("Invalid EnumSet type: " + type.toString());
}
} else {
throw new JsonIOException("Invalid EnumSet type: " + type.toString());
}
};
}
// Only support creation of EnumMap, but not of custom subtypes; for them type parameters
// and constructor parameter might have completely different meaning
else if (rawType == EnumMap.class) {
return new ObjectConstructor<T>() {
@Override
public T construct() {
if (type instanceof ParameterizedType) {
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
if (elementType instanceof Class) {
@SuppressWarnings({"unchecked", "rawtypes"})
T map = (T) new EnumMap((Class) elementType);
return map;
} else {
throw new JsonIOException("Invalid EnumMap type: " + type.toString());
}
return () -> {
if (type instanceof ParameterizedType) {
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
if (elementType instanceof Class) {
@SuppressWarnings({"unchecked", "rawtypes"})
T map = (T) new EnumMap((Class) elementType);
return map;
} else {
throw new JsonIOException("Invalid EnumMap type: " + type.toString());
}
} else {
throw new JsonIOException("Invalid EnumMap type: " + type.toString());
}
};
}
Expand Down Expand Up @@ -256,11 +234,8 @@ private static <T> ObjectConstructor<T> newDefaultConstructor(
+ " constructor is not accessible and ReflectionAccessFilter does not permit making"
+ " it accessible. Register an InstanceCreator or a TypeAdapter for this type, change"
+ " the visibility of the constructor or adjust the access filter.";
return new ObjectConstructor<T>() {
@Override
public T construct() {
throw new JsonIOException(message);
}
return () -> {
throw new JsonIOException(message);
};
}

Expand All @@ -277,45 +252,39 @@ public T construct() {
* (compared to directly throwing exception here), e.g. when runtime type
* of object is inaccessible, but compile-time type is accessible.
*/
return new ObjectConstructor<T>() {
@Override
public T construct() {
// New exception is created every time to avoid keeping reference
// to exception with potentially long stack trace, causing a
// memory leak
throw new JsonIOException(exceptionMessage);
}
return () -> {
// New exception is created every time to avoid keeping reference
// to exception with potentially long stack trace, causing a
// memory leak
throw new JsonIOException(exceptionMessage);
};
}
}

return new ObjectConstructor<T>() {
@Override
public T construct() {
try {
@SuppressWarnings("unchecked") // T is the same raw type as is requested
T newInstance = (T) constructor.newInstance();
return newInstance;
}
// Note: InstantiationException should be impossible because check at start of method made
// sure that class is not abstract
catch (InstantiationException e) {
throw new RuntimeException(
"Failed to invoke constructor '"
+ ReflectionHelper.constructorToString(constructor)
+ "' with no args",
e);
} catch (InvocationTargetException e) {
// TODO: don't wrap if cause is unchecked?
// TODO: JsonParseException ?
throw new RuntimeException(
"Failed to invoke constructor '"
+ ReflectionHelper.constructorToString(constructor)
+ "' with no args",
e.getCause());
} catch (IllegalAccessException e) {
throw ReflectionHelper.createExceptionForUnexpectedIllegalAccess(e);
}
return () -> {
try {
@SuppressWarnings("unchecked") // T is the same raw type as is requested
T newInstance = (T) constructor.newInstance();
return newInstance;
}
// Note: InstantiationException should be impossible because check at start of method made
// sure that class is not abstract
catch (InstantiationException e) {
throw new RuntimeException(
"Failed to invoke constructor '"
+ ReflectionHelper.constructorToString(constructor)
+ "' with no args",
e);
} catch (InvocationTargetException e) {
// TODO: don't wrap if cause is unchecked?
// TODO: JsonParseException ?
throw new RuntimeException(
"Failed to invoke constructor '"
+ ReflectionHelper.constructorToString(constructor)
+ "' with no args",
e.getCause());
} catch (IllegalAccessException e) {
throw ReflectionHelper.createExceptionForUnexpectedIllegalAccess(e);
}
};
}
Expand All @@ -335,74 +304,29 @@ private static <T> ObjectConstructor<T> newDefaultImplementationConstructor(

if (Collection.class.isAssignableFrom(rawType)) {
if (SortedSet.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() {
@Override
public T construct() {
return (T) new TreeSet<>();
}
};
return () -> (T) new TreeSet<>();
} else if (Set.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() {
@Override
public T construct() {
return (T) new LinkedHashSet<>();
}
};
return () -> (T) new LinkedHashSet<>();
} else if (Queue.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() {
@Override
public T construct() {
return (T) new ArrayDeque<>();
}
};
return () -> (T) new ArrayDeque<>();
} else {
return new ObjectConstructor<T>() {
@Override
public T construct() {
return (T) new ArrayList<>();
}
};
return () -> (T) new ArrayList<>();
}
}

if (Map.class.isAssignableFrom(rawType)) {
if (ConcurrentNavigableMap.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() {
@Override
public T construct() {
return (T) new ConcurrentSkipListMap<>();
}
};
return () -> (T) new ConcurrentSkipListMap<>();
} else if (ConcurrentMap.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() {
@Override
public T construct() {
return (T) new ConcurrentHashMap<>();
}
};
return () -> (T) new ConcurrentHashMap<>();
} else if (SortedMap.class.isAssignableFrom(rawType)) {
return new ObjectConstructor<T>() {
@Override
public T construct() {
return (T) new TreeMap<>();
}
};
return () -> (T) new TreeMap<>();
} else if (type instanceof ParameterizedType
&& !String.class.isAssignableFrom(
TypeToken.get(((ParameterizedType) type).getActualTypeArguments()[0]).getRawType())) {
return new ObjectConstructor<T>() {
@Override
public T construct() {
return (T) new LinkedHashMap<>();
}
};
return () -> (T) new LinkedHashMap<>();
} else {
return new ObjectConstructor<T>() {
@Override
public T construct() {
return (T) new LinkedTreeMap<>();
}
};
return () -> (T) new LinkedTreeMap<>();
}
}

Expand All @@ -411,21 +335,18 @@ public T construct() {

private <T> ObjectConstructor<T> newUnsafeAllocator(Class<? super T> rawType) {
if (useJdkUnsafe) {
return new ObjectConstructor<T>() {
@Override
public T construct() {
try {
@SuppressWarnings("unchecked")
T newInstance = (T) UnsafeAllocator.INSTANCE.newInstance(rawType);
return newInstance;
} catch (Exception e) {
throw new RuntimeException(
("Unable to create instance of "
+ rawType
+ ". Registering an InstanceCreator or a TypeAdapter for this type, or adding a"
+ " no-args constructor may fix this problem."),
e);
}
return () -> {
try {
@SuppressWarnings("unchecked")
T newInstance = (T) UnsafeAllocator.INSTANCE.newInstance(rawType);
return newInstance;
} catch (Exception e) {
throw new RuntimeException(
("Unable to create instance of "
+ rawType
+ ". Registering an InstanceCreator or a TypeAdapter for this type, or adding a"
+ " no-args constructor may fix this problem."),
e);
}
};
} else {
Expand All @@ -444,14 +365,11 @@ public T construct() {
" Or adjust your R8 configuration to keep the no-args constructor of the class.";
}

// Separate effectively final variable to allow usage in the anonymous class below
// Separate effectively final variable to allow usage in the lambda below
String exceptionMessageF = exceptionMessage;

return new ObjectConstructor<T>() {
@Override
public T construct() {
throw new JsonIOException(exceptionMessageF);
}
return () -> {
throw new JsonIOException(exceptionMessageF);
};
}
}
Expand Down
Loading