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;
|
|
|
|
|
2022-08-05 15:59:38 +02:00
|
|
|
import com.google.gson.internal.$Gson$Types;
|
2023-05-28 21:24:05 +02:00
|
|
|
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;
|
2023-08-14 23:26:05 +02:00
|
|
|
import java.lang.reflect.WildcardType;
|
2008-09-01 05:13:32 +02:00
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.Map;
|
2022-08-22 16:22:32 +02:00
|
|
|
import java.util.Objects;
|
2008-09-01 05:13:32 +02:00
|
|
|
|
|
|
|
/**
|
2023-11-06 20:59:01 +01: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.
|
2010-12-03 09:07:13 +01:00
|
|
|
*
|
2023-11-06 20:59:01 +01:00
|
|
|
* <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
|
|
|
*
|
2023-11-06 20:59:01 +01:00
|
|
|
* <p>{@code TypeToken<List<String>> list = new TypeToken<List<String>>() {};}
|
2010-12-03 09:07:13 +01:00
|
|
|
*
|
2023-11-06 20:59:01 +01:00
|
|
|
* <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
|
|
|
*
|
2023-11-06 20:59:01 +01: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
|
2010-12-03 09:07:13 +01:00
|
|
|
* @author Jesse Wilson
|
2008-09-01 05:13:32 +02:00
|
|
|
*/
|
2010-12-03 09:07:13 +01:00
|
|
|
public class TypeToken<T> {
|
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
|
|
|
|
|
|
|
/**
|
2023-11-06 20:59:01 +01:00
|
|
|
* Constructs a new type literal. Derives represented class from type parameter.
|
2008-09-01 05:13:32 +02:00
|
|
|
*
|
2023-11-06 20:59:01 +01: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:
|
2023-08-14 23:26:05 +02:00
|
|
|
*
|
2023-11-06 20:59:01 +01:00
|
|
|
* <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() {
|
2022-04-19 17:20:58 +02:00
|
|
|
this.type = getTypeTokenTypeArgument();
|
2011-04-06 02:26:57 +02:00
|
|
|
this.rawType = (Class<? super T>) $Gson$Types.getRawType(type);
|
2010-12-03 09:07:13 +01:00
|
|
|
this.hashCode = type.hashCode();
|
2008-09-01 05:13:32 +02:00
|
|
|
}
|
|
|
|
|
2023-11-06 20:59:01 +01:00
|
|
|
/** Unsafe. Constructs a type literal manually. */
|
2010-12-03 09:07:13 +01:00
|
|
|
@SuppressWarnings("unchecked")
|
2022-04-19 17:20:58 +02:00
|
|
|
private TypeToken(Type type) {
|
2022-08-22 16:22:32 +02:00
|
|
|
this.type = $Gson$Types.canonicalize(Objects.requireNonNull(type));
|
2011-04-06 02:26:57 +02:00
|
|
|
this.rawType = (Class<? super T>) $Gson$Types.getRawType(this.type);
|
2010-12-03 09:07:13 +01:00
|
|
|
this.hashCode = this.type.hashCode();
|
2008-09-01 05:13:32 +02:00
|
|
|
}
|
|
|
|
|
2023-08-14 23:26:05 +02:00
|
|
|
private static boolean isCapturingTypeVariablesForbidden() {
|
|
|
|
return !Objects.equals(System.getProperty("gson.allowCapturingTypeVariables"), "true");
|
|
|
|
}
|
|
|
|
|
2008-09-01 05:13:32 +02:00
|
|
|
/**
|
2023-11-06 20:59:01 +01: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
|
|
|
*/
|
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) {
|
2023-08-14 23:26:05 +02:00
|
|
|
Type typeArgument = $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
|
|
|
|
|
|
|
|
if (isCapturingTypeVariablesForbidden()) {
|
|
|
|
verifyNoTypeVariable(typeArgument);
|
|
|
|
}
|
|
|
|
return typeArgument;
|
2022-04-19 17:20:58 +02:00
|
|
|
}
|
2008-09-01 05:13:32 +02:00
|
|
|
}
|
2022-04-19 17:20:58 +02:00
|
|
|
// Check for raw TypeToken as superclass
|
|
|
|
else if (superclass == TypeToken.class) {
|
2023-11-06 20:59:01 +01:00
|
|
|
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"));
|
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
|
|
|
}
|
|
|
|
|
2023-08-14 23:26:05 +02:00
|
|
|
private static void verifyNoTypeVariable(Type type) {
|
|
|
|
if (type instanceof TypeVariable) {
|
|
|
|
TypeVariable<?> typeVariable = (TypeVariable<?>) type;
|
2023-11-06 20:59:01 +01:00
|
|
|
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"));
|
2023-08-14 23:26:05 +02:00
|
|
|
} 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) {
|
2023-11-06 20:59:01 +01:00
|
|
|
// 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");
|
2023-08-14 23:26:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-06 20:59:01 +01:00
|
|
|
/** Returns the raw (non-generic) type for this type. */
|
2010-12-03 09:07:13 +01:00
|
|
|
public final Class<? super T> getRawType() {
|
2008-09-01 05:13:32 +02:00
|
|
|
return rawType;
|
|
|
|
}
|
|
|
|
|
2023-11-06 20:59:01 +01:00
|
|
|
/** Gets underlying {@code Type} instance. */
|
2010-12-03 09:07:13 +01:00
|
|
|
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.
|
2010-12-03 19:12:26 +01:00
|
|
|
*
|
2023-11-06 20:59:01 +01:00
|
|
|
* @deprecated this implementation may be inconsistent with javac for types with wildcards.
|
2008-09-01 05:13:32 +02:00
|
|
|
*/
|
2010-12-03 19:12:26 +01: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.
|
2010-12-03 19:12:26 +01:00
|
|
|
*
|
2023-11-06 20:59:01 +01:00
|
|
|
* @deprecated this implementation may be inconsistent with javac for types with wildcards.
|
2008-09-01 05:13:32 +02:00
|
|
|
*/
|
2010-12-03 19:12:26 +01: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;
|
|
|
|
}
|
|
|
|
|
2009-09-23 19:45:16 +02:00
|
|
|
if (type instanceof Class<?>) {
|
2011-04-06 02:26:57 +02:00
|
|
|
return rawType.isAssignableFrom($Gson$Types.getRawType(from));
|
2008-09-01 05:13:32 +02:00
|
|
|
} else if (type instanceof ParameterizedType) {
|
2023-11-06 20:59:01 +01:00
|
|
|
return isAssignableFrom(from, (ParameterizedType) type, new HashMap<String, Type>());
|
2008-09-01 05:13:32 +02:00
|
|
|
} else if (type instanceof GenericArrayType) {
|
2011-04-06 02:26:57 +02:00
|
|
|
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.
|
2010-12-03 19:12:26 +01:00
|
|
|
*
|
2023-11-06 20:59:01 +01:00
|
|
|
* @deprecated this implementation may be inconsistent with javac for types with wildcards.
|
2008-09-01 05:13:32 +02:00
|
|
|
*/
|
2010-12-03 19:12:26 +01:00
|
|
|
@Deprecated
|
2008-09-01 05:13:32 +02:00
|
|
|
public boolean isAssignableFrom(TypeToken<?> token) {
|
|
|
|
return isAssignableFrom(token.getType());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-11-06 20:59:01 +01:00
|
|
|
* 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();
|
2009-09-23 19:45:16 +02:00
|
|
|
} 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;
|
|
|
|
}
|
2023-11-06 20:59:01 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-11-06 20:59:01 +01:00
|
|
|
/** 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.
|
2011-04-06 02:26:57 +02:00
|
|
|
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];
|
2009-09-23 19:45:16 +02:00
|
|
|
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()) {
|
2022-04-18 00:27:21 +02:00
|
|
|
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();
|
2022-04-18 00:27:21 +02:00
|
|
|
return isAssignableFrom(sType, to, new HashMap<>(typeVarMap));
|
2008-09-01 05:13:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-11-06 20:59:01 +01: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
|
|
|
*/
|
2023-11-06 20:59:01 +01: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;
|
|
|
|
}
|
|
|
|
|
2023-11-06 20:59:01 +01:00
|
|
|
private static AssertionError buildUnexpectedTypeError(Type token, Class<?>... expected) {
|
2010-12-03 09:07:13 +01:00
|
|
|
|
|
|
|
// Build exception message
|
2023-11-06 20:59:01 +01:00
|
|
|
StringBuilder exceptionMessage = new StringBuilder("Unexpected type. Expected one of: ");
|
2010-12-03 09:07:13 +01:00
|
|
|
for (Class<?> clazz : expected) {
|
|
|
|
exceptionMessage.append(clazz.getName()).append(", ");
|
|
|
|
}
|
2023-11-06 20:59:01 +01:00
|
|
|
exceptionMessage
|
|
|
|
.append("but got: ")
|
|
|
|
.append(token.getClass().getName())
|
|
|
|
.append(", for type token: ")
|
|
|
|
.append(token.toString())
|
|
|
|
.append('.');
|
2010-12-03 09:07:13 +01:00
|
|
|
|
|
|
|
return new AssertionError(exceptionMessage.toString());
|
|
|
|
}
|
|
|
|
|
2008-09-01 05:13:32 +02:00
|
|
|
/**
|
2023-11-06 20:59:01 +01: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
|
|
|
*/
|
2010-12-03 19:12:26 +01:00
|
|
|
private static boolean matches(Type from, Type to, Map<String, Type> typeMap) {
|
|
|
|
return to.equals(from)
|
|
|
|
|| (from instanceof TypeVariable
|
2023-11-06 20:59:01 +01:00
|
|
|
&& to.equals(typeMap.get(((TypeVariable<?>) from).getName())));
|
2008-09-01 05:13:32 +02:00
|
|
|
}
|
|
|
|
|
2023-11-06 20:59:01 +01:00
|
|
|
@Override
|
|
|
|
public final int hashCode() {
|
2010-12-03 09:07:13 +01:00
|
|
|
return this.hashCode;
|
|
|
|
}
|
|
|
|
|
2023-11-06 20:59:01 +01:00
|
|
|
@Override
|
|
|
|
public final boolean equals(Object o) {
|
|
|
|
return o instanceof TypeToken<?> && $Gson$Types.equals(type, ((TypeToken<?>) o).type);
|
2010-12-03 09:07:13 +01:00
|
|
|
}
|
|
|
|
|
2023-11-06 20:59:01 +01:00
|
|
|
@Override
|
|
|
|
public final String toString() {
|
2011-04-06 02:26:57 +02:00
|
|
|
return $Gson$Types.typeToString(type);
|
2010-12-03 09:07:13 +01:00
|
|
|
}
|
|
|
|
|
2023-11-06 20:59:01 +01:00
|
|
|
/** Gets type literal for the given {@code Type} instance. */
|
2010-12-03 09:07:13 +01:00
|
|
|
public static TypeToken<?> get(Type type) {
|
2022-04-18 00:27:21 +02:00
|
|
|
return new TypeToken<>(type);
|
2008-09-01 05:13:32 +02:00
|
|
|
}
|
|
|
|
|
2023-11-06 20:59:01 +01:00
|
|
|
/** Gets type literal for the given {@code Class} instance. */
|
2010-12-03 09:07:13 +01:00
|
|
|
public static <T> TypeToken<T> get(Class<T> type) {
|
2022-04-18 00:27:21 +02:00
|
|
|
return new TypeToken<>(type);
|
2010-12-03 09:07:13 +01:00
|
|
|
}
|
2016-08-12 18:11:57 +02:00
|
|
|
|
|
|
|
/**
|
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:
|
2023-11-06 20:59:01 +01:00
|
|
|
*
|
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>
|
2023-11-06 20:59:01 +01:00
|
|
|
*
|
2023-08-14 23:26:05 +02:00
|
|
|
* 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.
|
2022-08-05 15:59:38 +02:00
|
|
|
*
|
2023-07-25 22:00:26 +02:00
|
|
|
* <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)}.
|
|
|
|
*
|
2023-11-06 20:59:01 +01:00
|
|
|
* @throws IllegalArgumentException If {@code rawType} is not of type {@code Class}, or if the
|
|
|
|
* type arguments are invalid for the raw type
|
2016-08-12 18:11:57 +02:00
|
|
|
*/
|
|
|
|
public static TypeToken<?> getParameterized(Type rawType, Type... typeArguments) {
|
2022-08-22 16:22:32 +02:00
|
|
|
Objects.requireNonNull(rawType);
|
|
|
|
Objects.requireNonNull(typeArguments);
|
2022-08-05 15:59:38 +02:00
|
|
|
|
|
|
|
// 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();
|
|
|
|
|
2023-07-25 22:00:26 +02:00
|
|
|
int expectedArgsCount = typeVariables.length;
|
|
|
|
int actualArgsCount = typeArguments.length;
|
|
|
|
if (actualArgsCount != expectedArgsCount) {
|
2023-11-06 20:59:01 +01:00
|
|
|
throw new IllegalArgumentException(
|
|
|
|
rawClass.getName()
|
|
|
|
+ " requires "
|
|
|
|
+ expectedArgsCount
|
|
|
|
+ " type arguments, but got "
|
|
|
|
+ actualArgsCount);
|
2023-07-25 22:00:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// For legacy reasons create a TypeToken(Class) if the type is not generic
|
|
|
|
if (typeArguments.length == 0) {
|
|
|
|
return get(rawClass);
|
2023-06-24 00:24:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check for this here to avoid misleading exception thrown by ParameterizedTypeImpl
|
|
|
|
if ($Gson$Types.requiresOwnerType(rawType)) {
|
2023-11-06 20:59:01 +01:00
|
|
|
throw new IllegalArgumentException(
|
|
|
|
"Raw type "
|
|
|
|
+ rawClass.getName()
|
|
|
|
+ " is not supported because"
|
|
|
|
+ " it requires specifying an owner type");
|
2023-06-24 00:24:23 +02:00
|
|
|
}
|
|
|
|
|
2022-08-05 15:59:38 +02:00
|
|
|
for (int i = 0; i < expectedArgsCount; i++) {
|
2023-11-06 20:59:01 +01:00
|
|
|
Type typeArgument =
|
|
|
|
Objects.requireNonNull(typeArguments[i], "Type argument must not be null");
|
2022-08-05 15:59:38 +02:00
|
|
|
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)) {
|
2023-11-06 20:59:01 +01:00
|
|
|
throw new IllegalArgumentException(
|
|
|
|
"Type argument "
|
|
|
|
+ typeArgument
|
|
|
|
+ " does not satisfy bounds"
|
|
|
|
+ " for type variable "
|
|
|
|
+ typeVariable
|
|
|
|
+ " declared by "
|
|
|
|
+ rawType);
|
2022-08-05 15:59:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-18 00:27:21 +02:00
|
|
|
return new TypeToken<>($Gson$Types.newParameterizedTypeWithOwner(null, rawType, typeArguments));
|
2016-08-12 18:11:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets type literal for the array type whose elements are all instances of {@code componentType}.
|
|
|
|
*/
|
|
|
|
public static TypeToken<?> getArray(Type componentType) {
|
2022-04-18 00:27:21 +02:00
|
|
|
return new TypeToken<>($Gson$Types.arrayOf(componentType));
|
2016-08-12 18:11:57 +02:00
|
|
|
}
|
2008-09-01 05:13:32 +02:00
|
|
|
}
|