gson-comments/gson/src/main/java/com/google/gson/reflect/TypeToken.java

455 lines
16 KiB
Java
Raw Normal View History

2008-09-01 05:13:32 +02:00
/*
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson.reflect;
import com.google.gson.internal.$Gson$Types;
import com.google.gson.internal.TroubleshootingGuide;
2008-09-01 05:13:32 +02:00
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
2008-09-01 05:13:32 +02:00
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
2008-09-01 05:13:32 +02:00
/**
* Represents a generic type {@code T}. Java doesn't yet provide a way to represent generic types,
* so this class does. Forces clients to create a subclass of this class which enables retrieval the
* type information even at runtime.
*
* <p>For example, to create a type literal for {@code List<String>}, you can create an empty
* anonymous class:
2008-09-01 05:13:32 +02:00
*
* <p>{@code TypeToken<List<String>> list = new TypeToken<List<String>>() {};}
*
* <p>Capturing a type variable as type argument of an anonymous {@code TypeToken} subclass is not
* allowed, for example {@code TypeToken<List<T>>}. Due to type erasure the runtime type of a type
* variable is not available to Gson and therefore it cannot provide the functionality one might
* expect. This would give a false sense of type-safety at compile time and could lead to an
* unexpected {@code ClassCastException} at runtime.
2008-09-01 05:13:32 +02:00
*
* <p>If the type arguments of the parameterized type are only available at runtime, for example
* when you want to create a {@code List<E>} based on a {@code Class<E>} representing the element
* type, the method {@link #getParameterized(Type, Type...)} can be used.
Add Gson.fromJson(..., TypeToken) overloads (#1700) * Add Gson.fromJson(..., TypeToken) overloads Previously only Gson.fromJson(..., Type) existed which is however not type-safe since the generic type parameter T used for the return type is not bound. Since these methods are often used in the form gson.fromJson(..., new TypeToken<...>(){}.getType()) this commit now adds overloads which accept a TypeToken and are therefore more type-safe. Additional changes: - Fixed some grammar mistakes - Added javadoc @see tags - Consistently write "JSON" in uppercase - More precise placement of @SuppressWarnings("unchecked") * Add to Gson.fromJson javadoc that JSON is fully consumed The newly added documentation deliberately does not state which exception is thrown because Gson.assertFullConsumption could throw either a JsonIOException or a JsonSyntaxException. * Remove unnecessary wrapping and unwrapping as TypeToken in Gson.fromJson Since the actual implementation of Gson.fromJson is TypeToken based, the TypeToken variant overloads are now the "main" implementation and the other overloads delegate to them. Previously the Type variant overloads were the "main" implementation which caused `TypeToken.getType()` followed by `TypeToken.get(...)` when the TypeToken variant overloads were used. * Trim source code whitespaces * Fix Gson.fromJson(JsonReader, Class) not casting read Object To be consistent with the other Gson.fromJson(..., Class) overloads the method should cast the result. * Replace User Guide link in Gson documentation * Remove more references to fromJson(..., Type) * Extend documentation for fromJson(JsonReader, ...) * Replace some TypeToken.getType() usages * Address feedback; improve documentation * Remove fromJson(JsonReader, Class) again As noticed during review adding this method is source incompatible.
2022-09-19 15:47:11 +02:00
*
2008-09-01 05:13:32 +02:00
* @author Bob Lee
* @author Sven Mawson
* @author Jesse Wilson
2008-09-01 05:13:32 +02:00
*/
public class TypeToken<T> {
Improve `TypeToken` creation validation (#2072) * Add comments regarding multiple bounds of wildcard * Remove WildcardType check in getCollectionElementType The returned Type is never a wildcard due to the changes made to getSupertype by commit b1fb9ca9a1bea5440bc6a5b506ccf67236b08243. * Remove redundant getRawType call from MapTypeAdapterFactory getRawType(TypeToken.getType()) is the same as calling TypeToken.getRawType(). * Make TypeToken members private * Remove incorrect statement about TypeToken wildcards It is possible to use wildcards as part of the type argument, e.g.: `new TypeToken<List<? extends CharSequence>>() {}` * Only allow direct subclasses of TypeToken Previously subclasses of subclasses (...) of TypeToken were allowed which can behave incorrectly when retrieving the type argument, e.g.: class SubTypeToken<T> extends TypeToken<Integer> {} new SubTypeToken<String>() {}.getType() This returned `String` despite the class extending TypeToken<Integer>. * Throw exception when TypeToken captures type variable Due to type erasure the runtime type argument for a type variable is not available. Therefore there is no point in capturing a type variable and it might even give a false sense of type-safety. * Make $Gson$Types members private * Rename $Gson$Types.getGenericSupertype parameter Rename the method parameter to match the documentation of the method and to be similar to getSupertype(...). * Improve tests and handle raw TypeToken supertype better * Make some $Gson$Types members package-private again to prevent synthetic accessors * Remove TypeToken check for type variable As mentioned in review comments, there are cases during serialization where usage of the type variable is not so problematic (but still not ideal).
2022-04-19 17:20:58 +02:00
private final Class<? super T> rawType;
private final Type type;
private final int hashCode;
2008-09-01 05:13:32 +02:00
/**
* Constructs a new type literal. Derives represented class from type parameter.
2008-09-01 05:13:32 +02:00
*
* <p>Clients create an empty anonymous subclass. Doing so embeds the type parameter in the
* anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure, for
* example:
*
* <p>{@code new TypeToken<List<String>>() {}}
*
* @throws IllegalArgumentException If the anonymous {@code TypeToken} subclass captures a type
* variable, for example {@code TypeToken<List<T>>}. See the {@code TypeToken} class
* documentation for more details.
2008-09-01 05:13:32 +02:00
*/
@SuppressWarnings("unchecked")
protected TypeToken() {
Improve `TypeToken` creation validation (#2072) * Add comments regarding multiple bounds of wildcard * Remove WildcardType check in getCollectionElementType The returned Type is never a wildcard due to the changes made to getSupertype by commit b1fb9ca9a1bea5440bc6a5b506ccf67236b08243. * Remove redundant getRawType call from MapTypeAdapterFactory getRawType(TypeToken.getType()) is the same as calling TypeToken.getRawType(). * Make TypeToken members private * Remove incorrect statement about TypeToken wildcards It is possible to use wildcards as part of the type argument, e.g.: `new TypeToken<List<? extends CharSequence>>() {}` * Only allow direct subclasses of TypeToken Previously subclasses of subclasses (...) of TypeToken were allowed which can behave incorrectly when retrieving the type argument, e.g.: class SubTypeToken<T> extends TypeToken<Integer> {} new SubTypeToken<String>() {}.getType() This returned `String` despite the class extending TypeToken<Integer>. * Throw exception when TypeToken captures type variable Due to type erasure the runtime type argument for a type variable is not available. Therefore there is no point in capturing a type variable and it might even give a false sense of type-safety. * Make $Gson$Types members private * Rename $Gson$Types.getGenericSupertype parameter Rename the method parameter to match the documentation of the method and to be similar to getSupertype(...). * Improve tests and handle raw TypeToken supertype better * Make some $Gson$Types members package-private again to prevent synthetic accessors * Remove TypeToken check for type variable As mentioned in review comments, there are cases during serialization where usage of the type variable is not so problematic (but still not ideal).
2022-04-19 17:20:58 +02:00
this.type = getTypeTokenTypeArgument();
this.rawType = (Class<? super T>) $Gson$Types.getRawType(type);
this.hashCode = type.hashCode();
2008-09-01 05:13:32 +02:00
}
/** Unsafe. Constructs a type literal manually. */
@SuppressWarnings("unchecked")
Improve `TypeToken` creation validation (#2072) * Add comments regarding multiple bounds of wildcard * Remove WildcardType check in getCollectionElementType The returned Type is never a wildcard due to the changes made to getSupertype by commit b1fb9ca9a1bea5440bc6a5b506ccf67236b08243. * Remove redundant getRawType call from MapTypeAdapterFactory getRawType(TypeToken.getType()) is the same as calling TypeToken.getRawType(). * Make TypeToken members private * Remove incorrect statement about TypeToken wildcards It is possible to use wildcards as part of the type argument, e.g.: `new TypeToken<List<? extends CharSequence>>() {}` * Only allow direct subclasses of TypeToken Previously subclasses of subclasses (...) of TypeToken were allowed which can behave incorrectly when retrieving the type argument, e.g.: class SubTypeToken<T> extends TypeToken<Integer> {} new SubTypeToken<String>() {}.getType() This returned `String` despite the class extending TypeToken<Integer>. * Throw exception when TypeToken captures type variable Due to type erasure the runtime type argument for a type variable is not available. Therefore there is no point in capturing a type variable and it might even give a false sense of type-safety. * Make $Gson$Types members private * Rename $Gson$Types.getGenericSupertype parameter Rename the method parameter to match the documentation of the method and to be similar to getSupertype(...). * Improve tests and handle raw TypeToken supertype better * Make some $Gson$Types members package-private again to prevent synthetic accessors * Remove TypeToken check for type variable As mentioned in review comments, there are cases during serialization where usage of the type variable is not so problematic (but still not ideal).
2022-04-19 17:20:58 +02:00
private TypeToken(Type type) {
this.type = $Gson$Types.canonicalize(Objects.requireNonNull(type));
this.rawType = (Class<? super T>) $Gson$Types.getRawType(this.type);
this.hashCode = this.type.hashCode();
2008-09-01 05:13:32 +02:00
}
private static boolean isCapturingTypeVariablesForbidden() {
return !Objects.equals(System.getProperty("gson.allowCapturingTypeVariables"), "true");
}
2008-09-01 05:13:32 +02:00
/**
* Verifies that {@code this} is an instance of a direct subclass of TypeToken and returns the
* type argument for {@code T} in {@link $Gson$Types#canonicalize canonical form}.
2008-09-01 05:13:32 +02:00
*/
Improve `TypeToken` creation validation (#2072) * Add comments regarding multiple bounds of wildcard * Remove WildcardType check in getCollectionElementType The returned Type is never a wildcard due to the changes made to getSupertype by commit b1fb9ca9a1bea5440bc6a5b506ccf67236b08243. * Remove redundant getRawType call from MapTypeAdapterFactory getRawType(TypeToken.getType()) is the same as calling TypeToken.getRawType(). * Make TypeToken members private * Remove incorrect statement about TypeToken wildcards It is possible to use wildcards as part of the type argument, e.g.: `new TypeToken<List<? extends CharSequence>>() {}` * Only allow direct subclasses of TypeToken Previously subclasses of subclasses (...) of TypeToken were allowed which can behave incorrectly when retrieving the type argument, e.g.: class SubTypeToken<T> extends TypeToken<Integer> {} new SubTypeToken<String>() {}.getType() This returned `String` despite the class extending TypeToken<Integer>. * Throw exception when TypeToken captures type variable Due to type erasure the runtime type argument for a type variable is not available. Therefore there is no point in capturing a type variable and it might even give a false sense of type-safety. * Make $Gson$Types members private * Rename $Gson$Types.getGenericSupertype parameter Rename the method parameter to match the documentation of the method and to be similar to getSupertype(...). * Improve tests and handle raw TypeToken supertype better * Make some $Gson$Types members package-private again to prevent synthetic accessors * Remove TypeToken check for type variable As mentioned in review comments, there are cases during serialization where usage of the type variable is not so problematic (but still not ideal).
2022-04-19 17:20:58 +02:00
private Type getTypeTokenTypeArgument() {
Type superclass = getClass().getGenericSuperclass();
if (superclass instanceof ParameterizedType) {
ParameterizedType parameterized = (ParameterizedType) superclass;
if (parameterized.getRawType() == TypeToken.class) {
Type typeArgument = $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
if (isCapturingTypeVariablesForbidden()) {
verifyNoTypeVariable(typeArgument);
}
return typeArgument;
Improve `TypeToken` creation validation (#2072) * Add comments regarding multiple bounds of wildcard * Remove WildcardType check in getCollectionElementType The returned Type is never a wildcard due to the changes made to getSupertype by commit b1fb9ca9a1bea5440bc6a5b506ccf67236b08243. * Remove redundant getRawType call from MapTypeAdapterFactory getRawType(TypeToken.getType()) is the same as calling TypeToken.getRawType(). * Make TypeToken members private * Remove incorrect statement about TypeToken wildcards It is possible to use wildcards as part of the type argument, e.g.: `new TypeToken<List<? extends CharSequence>>() {}` * Only allow direct subclasses of TypeToken Previously subclasses of subclasses (...) of TypeToken were allowed which can behave incorrectly when retrieving the type argument, e.g.: class SubTypeToken<T> extends TypeToken<Integer> {} new SubTypeToken<String>() {}.getType() This returned `String` despite the class extending TypeToken<Integer>. * Throw exception when TypeToken captures type variable Due to type erasure the runtime type argument for a type variable is not available. Therefore there is no point in capturing a type variable and it might even give a false sense of type-safety. * Make $Gson$Types members private * Rename $Gson$Types.getGenericSupertype parameter Rename the method parameter to match the documentation of the method and to be similar to getSupertype(...). * Improve tests and handle raw TypeToken supertype better * Make some $Gson$Types members package-private again to prevent synthetic accessors * Remove TypeToken check for type variable As mentioned in review comments, there are cases during serialization where usage of the type variable is not so problematic (but still not ideal).
2022-04-19 17:20:58 +02:00
}
2008-09-01 05:13:32 +02:00
}
Improve `TypeToken` creation validation (#2072) * Add comments regarding multiple bounds of wildcard * Remove WildcardType check in getCollectionElementType The returned Type is never a wildcard due to the changes made to getSupertype by commit b1fb9ca9a1bea5440bc6a5b506ccf67236b08243. * Remove redundant getRawType call from MapTypeAdapterFactory getRawType(TypeToken.getType()) is the same as calling TypeToken.getRawType(). * Make TypeToken members private * Remove incorrect statement about TypeToken wildcards It is possible to use wildcards as part of the type argument, e.g.: `new TypeToken<List<? extends CharSequence>>() {}` * Only allow direct subclasses of TypeToken Previously subclasses of subclasses (...) of TypeToken were allowed which can behave incorrectly when retrieving the type argument, e.g.: class SubTypeToken<T> extends TypeToken<Integer> {} new SubTypeToken<String>() {}.getType() This returned `String` despite the class extending TypeToken<Integer>. * Throw exception when TypeToken captures type variable Due to type erasure the runtime type argument for a type variable is not available. Therefore there is no point in capturing a type variable and it might even give a false sense of type-safety. * Make $Gson$Types members private * Rename $Gson$Types.getGenericSupertype parameter Rename the method parameter to match the documentation of the method and to be similar to getSupertype(...). * Improve tests and handle raw TypeToken supertype better * Make some $Gson$Types members package-private again to prevent synthetic accessors * Remove TypeToken check for type variable As mentioned in review comments, there are cases during serialization where usage of the type variable is not so problematic (but still not ideal).
2022-04-19 17:20:58 +02:00
// Check for raw TypeToken as superclass
else if (superclass == TypeToken.class) {
throw new IllegalStateException(
"TypeToken must be created with a type argument: new TypeToken<...>() {}; When using code"
+ " shrinkers (ProGuard, R8, ...) make sure that generic signatures are preserved.\n"
+ "See "
+ TroubleshootingGuide.createUrl("type-token-raw"));
Improve `TypeToken` creation validation (#2072) * Add comments regarding multiple bounds of wildcard * Remove WildcardType check in getCollectionElementType The returned Type is never a wildcard due to the changes made to getSupertype by commit b1fb9ca9a1bea5440bc6a5b506ccf67236b08243. * Remove redundant getRawType call from MapTypeAdapterFactory getRawType(TypeToken.getType()) is the same as calling TypeToken.getRawType(). * Make TypeToken members private * Remove incorrect statement about TypeToken wildcards It is possible to use wildcards as part of the type argument, e.g.: `new TypeToken<List<? extends CharSequence>>() {}` * Only allow direct subclasses of TypeToken Previously subclasses of subclasses (...) of TypeToken were allowed which can behave incorrectly when retrieving the type argument, e.g.: class SubTypeToken<T> extends TypeToken<Integer> {} new SubTypeToken<String>() {}.getType() This returned `String` despite the class extending TypeToken<Integer>. * Throw exception when TypeToken captures type variable Due to type erasure the runtime type argument for a type variable is not available. Therefore there is no point in capturing a type variable and it might even give a false sense of type-safety. * Make $Gson$Types members private * Rename $Gson$Types.getGenericSupertype parameter Rename the method parameter to match the documentation of the method and to be similar to getSupertype(...). * Improve tests and handle raw TypeToken supertype better * Make some $Gson$Types members package-private again to prevent synthetic accessors * Remove TypeToken check for type variable As mentioned in review comments, there are cases during serialization where usage of the type variable is not so problematic (but still not ideal).
2022-04-19 17:20:58 +02:00
}
// User created subclass of subclass of TypeToken
throw new IllegalStateException("Must only create direct subclasses of TypeToken");
2008-09-01 05:13:32 +02:00
}
private static void verifyNoTypeVariable(Type type) {
if (type instanceof TypeVariable) {
TypeVariable<?> typeVariable = (TypeVariable<?>) type;
throw new IllegalArgumentException(
"TypeToken type argument must not contain a type variable; captured type variable "
+ typeVariable.getName()
+ " declared by "
+ typeVariable.getGenericDeclaration()
+ "\nSee "
+ TroubleshootingGuide.createUrl("typetoken-type-variable"));
} else if (type instanceof GenericArrayType) {
verifyNoTypeVariable(((GenericArrayType) type).getGenericComponentType());
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Type ownerType = parameterizedType.getOwnerType();
if (ownerType != null) {
verifyNoTypeVariable(ownerType);
}
for (Type typeArgument : parameterizedType.getActualTypeArguments()) {
verifyNoTypeVariable(typeArgument);
}
} else if (type instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) type;
for (Type bound : wildcardType.getLowerBounds()) {
verifyNoTypeVariable(bound);
}
for (Type bound : wildcardType.getUpperBounds()) {
verifyNoTypeVariable(bound);
}
} else if (type == null) {
// Occurs in Eclipse IDE and certain Java versions (e.g. Java 11.0.18) when capturing type
// variable declared by method of local class, see
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/975
throw new IllegalArgumentException(
"TypeToken captured `null` as type argument; probably a compiler / runtime bug");
}
}
/** Returns the raw (non-generic) type for this type. */
public final Class<? super T> getRawType() {
2008-09-01 05:13:32 +02:00
return rawType;
}
/** Gets underlying {@code Type} instance. */
public final Type getType() {
2008-09-01 05:13:32 +02:00
return type;
}
/**
* Check if this type is assignable from the given class object.
*
* @deprecated this implementation may be inconsistent with javac for types with wildcards.
2008-09-01 05:13:32 +02:00
*/
@Deprecated
2008-09-01 05:13:32 +02:00
public boolean isAssignableFrom(Class<?> cls) {
return isAssignableFrom((Type) cls);
}
/**
* Check if this type is assignable from the given Type.
*
* @deprecated this implementation may be inconsistent with javac for types with wildcards.
2008-09-01 05:13:32 +02:00
*/
@Deprecated
2008-09-01 05:13:32 +02:00
public boolean isAssignableFrom(Type from) {
if (from == null) {
return false;
}
if (type.equals(from)) {
return true;
}
if (type instanceof Class<?>) {
return rawType.isAssignableFrom($Gson$Types.getRawType(from));
2008-09-01 05:13:32 +02:00
} else if (type instanceof ParameterizedType) {
return isAssignableFrom(from, (ParameterizedType) type, new HashMap<String, Type>());
2008-09-01 05:13:32 +02:00
} else if (type instanceof GenericArrayType) {
return rawType.isAssignableFrom($Gson$Types.getRawType(from))
2008-09-01 05:13:32 +02:00
&& isAssignableFrom(from, (GenericArrayType) type);
} else {
throw buildUnexpectedTypeError(
type, Class.class, ParameterizedType.class, GenericArrayType.class);
}
}
/**
* Check if this type is assignable from the given type token.
*
* @deprecated this implementation may be inconsistent with javac for types with wildcards.
2008-09-01 05:13:32 +02:00
*/
@Deprecated
2008-09-01 05:13:32 +02:00
public boolean isAssignableFrom(TypeToken<?> token) {
return isAssignableFrom(token.getType());
}
/**
* Private helper function that performs some assignability checks for the provided
* GenericArrayType.
2008-09-01 05:13:32 +02:00
*/
private static boolean isAssignableFrom(Type from, GenericArrayType to) {
Type toGenericComponentType = to.getGenericComponentType();
if (toGenericComponentType instanceof ParameterizedType) {
Type t = from;
if (from instanceof GenericArrayType) {
t = ((GenericArrayType) from).getGenericComponentType();
} else if (from instanceof Class<?>) {
2008-09-01 05:13:32 +02:00
Class<?> classType = (Class<?>) from;
while (classType.isArray()) {
classType = classType.getComponentType();
}
t = classType;
}
return isAssignableFrom(
t, (ParameterizedType) toGenericComponentType, new HashMap<String, Type>());
2008-09-01 05:13:32 +02:00
}
// No generic defined on "to"; therefore, return true and let other
// checks determine assignability
return true;
}
/** Private recursive helper function to actually do the type-safe checking of assignability. */
private static boolean isAssignableFrom(
Type from, ParameterizedType to, Map<String, Type> typeVarMap) {
2008-09-01 05:13:32 +02:00
if (from == null) {
return false;
}
if (to.equals(from)) {
return true;
}
// First figure out the class and any type information.
Class<?> clazz = $Gson$Types.getRawType(from);
2008-09-01 05:13:32 +02:00
ParameterizedType ptype = null;
if (from instanceof ParameterizedType) {
ptype = (ParameterizedType) from;
}
// Load up parameterized variable info if it was parameterized.
if (ptype != null) {
Type[] tArgs = ptype.getActualTypeArguments();
TypeVariable<?>[] tParams = clazz.getTypeParameters();
for (int i = 0; i < tArgs.length; i++) {
Type arg = tArgs[i];
TypeVariable<?> var = tParams[i];
while (arg instanceof TypeVariable<?>) {
2008-09-01 05:13:32 +02:00
TypeVariable<?> v = (TypeVariable<?>) arg;
arg = typeVarMap.get(v.getName());
}
typeVarMap.put(var.getName(), arg);
}
// check if they are equivalent under our current mapping.
if (typeEquals(ptype, to, typeVarMap)) {
return true;
}
}
for (Type itype : clazz.getGenericInterfaces()) {
if (isAssignableFrom(itype, to, new HashMap<>(typeVarMap))) {
2008-09-01 05:13:32 +02:00
return true;
}
}
// Interfaces didn't work, try the superclass.
Type sType = clazz.getGenericSuperclass();
return isAssignableFrom(sType, to, new HashMap<>(typeVarMap));
2008-09-01 05:13:32 +02:00
}
/**
* Checks if two parameterized types are exactly equal, under the variable replacement described
* in the typeVarMap.
2008-09-01 05:13:32 +02:00
*/
private static boolean typeEquals(
ParameterizedType from, ParameterizedType to, Map<String, Type> typeVarMap) {
2008-09-01 05:13:32 +02:00
if (from.getRawType().equals(to.getRawType())) {
Type[] fromArgs = from.getActualTypeArguments();
Type[] toArgs = to.getActualTypeArguments();
for (int i = 0; i < fromArgs.length; i++) {
if (!matches(fromArgs[i], toArgs[i], typeVarMap)) {
return false;
}
}
return true;
}
return false;
}
private static AssertionError buildUnexpectedTypeError(Type token, Class<?>... expected) {
// Build exception message
StringBuilder exceptionMessage = new StringBuilder("Unexpected type. Expected one of: ");
for (Class<?> clazz : expected) {
exceptionMessage.append(clazz.getName()).append(", ");
}
exceptionMessage
.append("but got: ")
.append(token.getClass().getName())
.append(", for type token: ")
.append(token.toString())
.append('.');
return new AssertionError(exceptionMessage.toString());
}
2008-09-01 05:13:32 +02:00
/**
* Checks if two types are the same or are equivalent under a variable mapping given in the type
* map that was provided.
2008-09-01 05:13:32 +02:00
*/
private static boolean matches(Type from, Type to, Map<String, Type> typeMap) {
return to.equals(from)
|| (from instanceof TypeVariable
&& to.equals(typeMap.get(((TypeVariable<?>) from).getName())));
2008-09-01 05:13:32 +02:00
}
@Override
public final int hashCode() {
return this.hashCode;
}
@Override
public final boolean equals(Object o) {
return o instanceof TypeToken<?> && $Gson$Types.equals(type, ((TypeToken<?>) o).type);
}
@Override
public final String toString() {
return $Gson$Types.typeToString(type);
}
/** Gets type literal for the given {@code Type} instance. */
public static TypeToken<?> get(Type type) {
return new TypeToken<>(type);
2008-09-01 05:13:32 +02:00
}
/** Gets type literal for the given {@code Class} instance. */
public static <T> TypeToken<T> get(Class<T> type) {
return new TypeToken<>(type);
}
/**
Add Gson.fromJson(..., TypeToken) overloads (#1700) * Add Gson.fromJson(..., TypeToken) overloads Previously only Gson.fromJson(..., Type) existed which is however not type-safe since the generic type parameter T used for the return type is not bound. Since these methods are often used in the form gson.fromJson(..., new TypeToken<...>(){}.getType()) this commit now adds overloads which accept a TypeToken and are therefore more type-safe. Additional changes: - Fixed some grammar mistakes - Added javadoc @see tags - Consistently write "JSON" in uppercase - More precise placement of @SuppressWarnings("unchecked") * Add to Gson.fromJson javadoc that JSON is fully consumed The newly added documentation deliberately does not state which exception is thrown because Gson.assertFullConsumption could throw either a JsonIOException or a JsonSyntaxException. * Remove unnecessary wrapping and unwrapping as TypeToken in Gson.fromJson Since the actual implementation of Gson.fromJson is TypeToken based, the TypeToken variant overloads are now the "main" implementation and the other overloads delegate to them. Previously the Type variant overloads were the "main" implementation which caused `TypeToken.getType()` followed by `TypeToken.get(...)` when the TypeToken variant overloads were used. * Trim source code whitespaces * Fix Gson.fromJson(JsonReader, Class) not casting read Object To be consistent with the other Gson.fromJson(..., Class) overloads the method should cast the result. * Replace User Guide link in Gson documentation * Remove more references to fromJson(..., Type) * Extend documentation for fromJson(JsonReader, ...) * Replace some TypeToken.getType() usages * Address feedback; improve documentation * Remove fromJson(JsonReader, Class) again As noticed during review adding this method is source incompatible.
2022-09-19 15:47:11 +02:00
* Gets a type literal for the parameterized type represented by applying {@code typeArguments} to
* {@code rawType}. This is mainly intended for situations where the type arguments are not
* available at compile time. The following example shows how a type token for {@code Map<K, V>}
* can be created:
*
Add Gson.fromJson(..., TypeToken) overloads (#1700) * Add Gson.fromJson(..., TypeToken) overloads Previously only Gson.fromJson(..., Type) existed which is however not type-safe since the generic type parameter T used for the return type is not bound. Since these methods are often used in the form gson.fromJson(..., new TypeToken<...>(){}.getType()) this commit now adds overloads which accept a TypeToken and are therefore more type-safe. Additional changes: - Fixed some grammar mistakes - Added javadoc @see tags - Consistently write "JSON" in uppercase - More precise placement of @SuppressWarnings("unchecked") * Add to Gson.fromJson javadoc that JSON is fully consumed The newly added documentation deliberately does not state which exception is thrown because Gson.assertFullConsumption could throw either a JsonIOException or a JsonSyntaxException. * Remove unnecessary wrapping and unwrapping as TypeToken in Gson.fromJson Since the actual implementation of Gson.fromJson is TypeToken based, the TypeToken variant overloads are now the "main" implementation and the other overloads delegate to them. Previously the Type variant overloads were the "main" implementation which caused `TypeToken.getType()` followed by `TypeToken.get(...)` when the TypeToken variant overloads were used. * Trim source code whitespaces * Fix Gson.fromJson(JsonReader, Class) not casting read Object To be consistent with the other Gson.fromJson(..., Class) overloads the method should cast the result. * Replace User Guide link in Gson documentation * Remove more references to fromJson(..., Type) * Extend documentation for fromJson(JsonReader, ...) * Replace some TypeToken.getType() usages * Address feedback; improve documentation * Remove fromJson(JsonReader, Class) again As noticed during review adding this method is source incompatible.
2022-09-19 15:47:11 +02:00
* <pre>{@code
* Class<K> keyClass = ...;
* Class<V> valueClass = ...;
* TypeToken<?> mapTypeToken = TypeToken.getParameterized(Map.class, keyClass, valueClass);
* }</pre>
*
* As seen here the result is a {@code TypeToken<?>}; this method cannot provide any type-safety,
Add Gson.fromJson(..., TypeToken) overloads (#1700) * Add Gson.fromJson(..., TypeToken) overloads Previously only Gson.fromJson(..., Type) existed which is however not type-safe since the generic type parameter T used for the return type is not bound. Since these methods are often used in the form gson.fromJson(..., new TypeToken<...>(){}.getType()) this commit now adds overloads which accept a TypeToken and are therefore more type-safe. Additional changes: - Fixed some grammar mistakes - Added javadoc @see tags - Consistently write "JSON" in uppercase - More precise placement of @SuppressWarnings("unchecked") * Add to Gson.fromJson javadoc that JSON is fully consumed The newly added documentation deliberately does not state which exception is thrown because Gson.assertFullConsumption could throw either a JsonIOException or a JsonSyntaxException. * Remove unnecessary wrapping and unwrapping as TypeToken in Gson.fromJson Since the actual implementation of Gson.fromJson is TypeToken based, the TypeToken variant overloads are now the "main" implementation and the other overloads delegate to them. Previously the Type variant overloads were the "main" implementation which caused `TypeToken.getType()` followed by `TypeToken.get(...)` when the TypeToken variant overloads were used. * Trim source code whitespaces * Fix Gson.fromJson(JsonReader, Class) not casting read Object To be consistent with the other Gson.fromJson(..., Class) overloads the method should cast the result. * Replace User Guide link in Gson documentation * Remove more references to fromJson(..., Type) * Extend documentation for fromJson(JsonReader, ...) * Replace some TypeToken.getType() usages * Address feedback; improve documentation * Remove fromJson(JsonReader, Class) again As noticed during review adding this method is source incompatible.
2022-09-19 15:47:11 +02:00
* and care must be taken to pass in the correct number of type arguments.
*
* <p>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}, or if the
* type arguments are invalid for the raw type
*/
public static TypeToken<?> getParameterized(Type rawType, Type... typeArguments) {
Objects.requireNonNull(rawType);
Objects.requireNonNull(typeArguments);
// Perform basic validation here because this is the only public API where users
// can create malformed parameterized types
if (!(rawType instanceof Class)) {
// See also https://bugs.openjdk.org/browse/JDK-8250659
throw new IllegalArgumentException("rawType must be of type Class, but was " + rawType);
}
Class<?> rawClass = (Class<?>) rawType;
TypeVariable<?>[] typeVariables = rawClass.getTypeParameters();
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
if ($Gson$Types.requiresOwnerType(rawType)) {
throw new IllegalArgumentException(
"Raw type "
+ rawClass.getName()
+ " is not supported because"
+ " it requires specifying an owner type");
}
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);
TypeVariable<?> typeVariable = typeVariables[i];
for (Type bound : typeVariable.getBounds()) {
Class<?> rawBound = $Gson$Types.getRawType(bound);
if (!rawBound.isAssignableFrom(rawTypeArgument)) {
throw new IllegalArgumentException(
"Type argument "
+ typeArgument
+ " does not satisfy bounds"
+ " for type variable "
+ typeVariable
+ " declared by "
+ rawType);
}
}
}
return new TypeToken<>($Gson$Types.newParameterizedTypeWithOwner(null, rawType, typeArguments));
}
/**
* Gets type literal for the array type whose elements are all instances of {@code componentType}.
*/
public static TypeToken<?> getArray(Type componentType) {
return new TypeToken<>($Gson$Types.arrayOf(componentType));
}
2008-09-01 05:13:32 +02:00
}