Skip to content

Commit

Permalink
Replace anonymous class creation with lambdas in ConstructorConstruct…
Browse files Browse the repository at this point in the history
…or (#2763)

Replace anonymous class creation with lambdas in ConstructorConstructor

Replace anonymous class creation with lambdas in ConstructorConstructor

Replace anonymous class creation with lambdas in ConstructorConstructor

Co-authored-by: Éamonn McManus <emcmanus@google.com>
  • Loading branch information
panic08 and eamonnmcmanus authored Oct 18, 2024
1 parent 73768be commit 2dc29f7
Showing 1 changed file with 79 additions and 161 deletions.
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

0 comments on commit 2dc29f7

Please sign in to comment.