diff --git a/gson/src/main/java/com/google/gson/reflect/TypeToken.java b/gson/src/main/java/com/google/gson/reflect/TypeToken.java index 4a695666..3921a70b 100644 --- a/gson/src/main/java/com/google/gson/reflect/TypeToken.java +++ b/gson/src/main/java/com/google/gson/reflect/TypeToken.java @@ -337,9 +337,12 @@ public class TypeToken { * As seen here the result is a {@code TypeToken}; this method cannot provide any type safety, * and care must be taken to pass in the correct number of type arguments. * + *

If {@code rawType} is a non-generic class and no type arguments are provided, this method + * simply delegates to {@link #get(Class)} and creates a {@code TypeToken(Class)}. + * * @throws IllegalArgumentException - * If {@code rawType} is not of type {@code Class}, if it is not a generic type, or if the - * type arguments are invalid for the raw type + * If {@code rawType} is not of type {@code Class}, or if the type arguments are invalid for + * the raw type */ public static TypeToken getParameterized(Type rawType, Type... typeArguments) { Objects.requireNonNull(rawType); @@ -354,10 +357,16 @@ public class TypeToken { Class rawClass = (Class) rawType; TypeVariable[] typeVariables = rawClass.getTypeParameters(); - // Note: Does not check if owner type of rawType is generic because this factory method - // does not support specifying owner type - if (typeVariables.length == 0) { - throw new IllegalArgumentException(rawClass.getName() + " is not a generic type"); + int expectedArgsCount = typeVariables.length; + int actualArgsCount = typeArguments.length; + if (actualArgsCount != expectedArgsCount) { + throw new IllegalArgumentException(rawClass.getName() + " requires " + expectedArgsCount + + " type arguments, but got " + actualArgsCount); + } + + // For legacy reasons create a TypeToken(Class) if the type is not generic + if (typeArguments.length == 0) { + return get(rawClass); } // Check for this here to avoid misleading exception thrown by ParameterizedTypeImpl @@ -366,13 +375,6 @@ public class TypeToken { + " it requires specifying an owner type"); } - int expectedArgsCount = typeVariables.length; - int actualArgsCount = typeArguments.length; - if (actualArgsCount != expectedArgsCount) { - throw new IllegalArgumentException(rawClass.getName() + " requires " + expectedArgsCount + - " type arguments, but got " + actualArgsCount); - } - for (int i = 0; i < expectedArgsCount; i++) { Type typeArgument = Objects.requireNonNull(typeArguments[i], "Type argument must not be null"); Class rawTypeArgument = $Gson$Types.getRawType(typeArgument); diff --git a/gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java b/gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java index d38f05b1..4c1ccf0d 100644 --- a/gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java +++ b/gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java @@ -146,6 +146,9 @@ public final class TypeTokenTest { class LocalGenericClass {} TypeToken expectedLocalType = new TypeToken>() {}; assertThat(TypeToken.getParameterized(LocalGenericClass.class, Integer.class)).isEqualTo(expectedLocalType); + + // For legacy reasons, if requesting parameterized type for non-generic class, create a `TypeToken(Class)` + assertThat(TypeToken.getParameterized(String.class)).isEqualTo(TypeToken.get(String.class)); } @Test @@ -158,7 +161,7 @@ public final class TypeTokenTest { assertThat(e).hasMessageThat().isEqualTo("rawType must be of type Class, but was java.lang.String[]"); e = assertThrows(IllegalArgumentException.class, () -> TypeToken.getParameterized(String.class, Number.class)); - assertThat(e).hasMessageThat().isEqualTo("java.lang.String is not a generic type"); + assertThat(e).hasMessageThat().isEqualTo("java.lang.String requires 0 type arguments, but got 1"); e = assertThrows(IllegalArgumentException.class, () -> TypeToken.getParameterized(List.class, new Type[0])); assertThat(e).hasMessageThat().isEqualTo("java.util.List requires 1 type arguments, but got 0");