Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Resolves #1219 (by preventing to capture type variables using
TypeToken
anonymous subclass)Tries to improve type handling of Gson. Related to #1741 (comment).
Currently still a draft because there are certain further improvements which I am not sure how to implement (listed below). This would probably also have to include the changes done by #1391.
TODO
Type variables
The following type variable logic should also apply to type variables declared by methods and constructors.
Serialization
When serializing should get the bounds of the type variable:
T extends List<U>
) apply this logic recursively. However, if there is a cycle, e.g.X extends Comparable<X>
, should throw exception.TypeToken
and even if they could it might result in conflicts between different libraries when for example multiple libraries register differentCharSequence & Number
adapters which behave differently.Deserialization
When deserializing and the type is not raw should always throw exception because at runtime the type argument for that type variable is likely a subtype of the bound. This would then at unrelated places cause unexpected
ClassCastException
s so it would be better if Gson already failed on deserialization.If the type is raw then the same logic for resolving the type variable as described in the "Serialization" section above should be used.
Implementation notes
Throwing the exceptions directly on calls to
$Gson$Types.resolve(Type, Class<?>, Type)
should not be done because the problematic type variable might not be considered by the type adapter. For example for a classMyGenericClass<T>
, if the custom adapter forMyGenericClass
does not even consider the type variable then it is not an issue that an unresolved type variable exists. So maybe the exception should only be thrown on calls toGson.getAdapter(TypeToken)
.However, the other problem is then to determine when the caller is serializing and when it is deserializing. This could be solved in a somewhat reliable way by having the
ReflectiveTypeAdapterFactory
resolve the type twice, once for serialization and a second time for deserialization.$Gson$Types.resolve
could then return different resolved types which are then detected byGson.getAdapter
and cause it to throw an exception.Wildcards
Wildcards without or with lower bounds
Wildcards without bounds or with lower bounds should be treated as if the class declaring the type variable for which the wildcard is a type argument is raw, i.e.
private List<?> f
should be treated likeprivate List f
(raw type) as described in the "Type variables" section above.However, the bound of that wildcard should be resolved with the respective type arguments for the other type variables. For example:
Wildcards with upper bounds
Due to JDK-8250936 handling of wildcards with upper bounds is slightly more complicated. First the wildcard should be treated like "Wildcards without or with lower bounds" described above. The resolved type should then be compared with the resolved type of the wildcard bound and the more specific one of them should be used as resolved type.
For example: