Skip to content

Commit

Permalink
Replace anonymous class creation with lambdas in ConstructorConstructor
Browse files Browse the repository at this point in the history
Replace anonymous class creation with lambdas in ConstructorConstructor
  • Loading branch information
panic08 committed Oct 16, 2024
1 parent 23bf512 commit 30ba3be
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 162 deletions.
238 changes: 78 additions & 160 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 @@ -447,11 +368,8 @@ public T construct() {
// Separate effectively final variable to allow usage in the anonymous class below
String exceptionMessageF = exceptionMessage;

return new ObjectConstructor<T>() {
@Override
public T construct() {
throw new JsonIOException(exceptionMessageF);
}
return () -> {
throw new JsonIOException(exceptionMessageF);
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ private interface Interface {}
public void testGet_AbstractClassNoArgConstructor() {
ObjectConstructor<AbstractClass> constructor =
constructorConstructor.get(TypeToken.get(AbstractClass.class));
var e = assertThrows(RuntimeException.class, () -> constructor.construct());
var e = assertThrows(RuntimeException.class, constructor::construct);
assertThat(e)
.hasMessageThat()
.isEqualTo(
Expand All @@ -56,7 +56,7 @@ public void testGet_AbstractClassNoArgConstructor() {
public void testGet_Interface() {
ObjectConstructor<Interface> constructor =
constructorConstructor.get(TypeToken.get(Interface.class));
var e = assertThrows(RuntimeException.class, () -> constructor.construct());
var e = assertThrows(RuntimeException.class, constructor::construct);
assertThat(e)
.hasMessageThat()
.isEqualTo(
Expand Down

0 comments on commit 30ba3be

Please sign in to comment.