Skip to content

Commit

Permalink
Use hyphen for term "type-safe"
Browse files Browse the repository at this point in the history
Not completely sure if that is grammatically correct, but it might make the
text a bit easier to understand.
  • Loading branch information
Marcono1234 committed Jun 23, 2023
1 parent febe8f8 commit 9bc4255
Show file tree
Hide file tree
Showing 4 changed files with 7 additions and 8 deletions.
9 changes: 4 additions & 5 deletions Troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,18 +332,17 @@ Note: If the class which you are trying to deserialize is actually abstract, the

**Symptom:** An exception with the message 'TypeToken type argument must not contain a type variable' is thrown

**Reason:** This exception is thrown when you create an anonymous `TypeToken` subclass which captures a type variable, for example `new TypeToken<List<T>>() {}` (where `T` is a type variable). At compile time such code looks safe and you can use the type `List<T>` without any warnings. However, this code is not actually type safe because at runtime due to [type erasure](https://dev.java/learn/generics/type-erasure/) only the upper bound of the type variable is available. For the previous example that would be `List<Object>`. When using such a `TypeToken` with any Gson methods performing deserialization this would lead to confusing and difficult to debug `ClassCastException`s. For serialization it can in some cases also lead to undesired results.
**Reason:** This exception is thrown when you create an anonymous `TypeToken` subclass which captures a type variable, for example `new TypeToken<List<T>>() {}` (where `T` is a type variable). At compile time such code looks safe and you can use the type `List<T>` without any warnings. However, this code is not actually type-safe because at runtime due to [type erasure](https://dev.java/learn/generics/type-erasure/) only the upper bound of the type variable is available. For the previous example that would be `List<Object>`. When using such a `TypeToken` with any Gson methods performing deserialization this would lead to confusing and difficult to debug `ClassCastException`s. For serialization it can in some cases also lead to undesired results.

Note: Earlier version of Gson unfortunately did not prevent capturing type variables, which caused many users to unwittingly write type unsafe code.
Note: Earlier version of Gson unfortunately did not prevent capturing type variables, which caused many users to unwittingly write type-unsafe code.

**Solution:**

- Use [`TypeToken.getParameterized(...)`](https://www.javadoc.io/doc/com.google.code.gson/gson/latest/com.google.gson/com/google/gson/reflect/TypeToken.html#getParameterized(java.lang.reflect.Type,java.lang.reflect.Type...)), for example `TypeToken.getParameterized(List.class, elementType)` where `elementType` is a type you have to provide separately.
- For Kotlin users: Use [`reified` type parameters](https://kotlinlang.org/docs/inline-functions.html#reified-type-parameters), that means change `<T>` to `<reified T>`, if possible. If you have a chain of functions with type parameters you will probably have to make all of them `reified`.
- If you don't actually use Gson's `TypeToken` for any Gson method, use a general purpose 'type token' implementation provided by a different library instead, for example Guava's [`com.google.common.reflect.TypeToken`](https://javadoc.io/doc/com.google.guava/guava/latest/com/google/common/reflect/TypeToken.html).

For backward compatibility it is possible to restore Gson's old behavior of allowing `TypeToken` to capture type variables by setting the [system property](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/System.html#setProperty(java.lang.String,java.lang.String)) `gson.allow-capturing-type-variables` to `"true"`.
**Important:**
For backward compatibility it is possible to restore Gson's old behavior of allowing `TypeToken` to capture type variables by setting the [system property](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/System.html#setProperty(java.lang.String,java.lang.String)) `gson.allow-capturing-type-variables` to `"true"`, **however**:

- This does not solve any of the type safety problems mentioned above; in the long term you should prefer one of the other solutions listed above. This system property might be removed in future Gson versions.
- This does not solve any of the type-safety problems mentioned above; in the long term you should prefer one of the other solutions listed above. This system property might be removed in future Gson versions.
- You should only ever set the property to `"true"`, but never to any other value or manually clear it. Otherwise this might counteract any libraries you are using which might have deliberately set the system property because they rely on its behavior.
2 changes: 1 addition & 1 deletion gson/src/main/java/com/google/gson/reflect/TypeToken.java
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ public static <T> TypeToken<T> get(Class<T> type) {
* 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,
* 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.
*
* @throws IllegalArgumentException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ <M> void testMethodTypeVariable() throws Exception {
+ " captured type variable M declared by " + testMethod
+ "\nSee https://github.com/google/gson/blob/master/Troubleshooting.md#typetoken-type-variable",
// Note: When running this test in Eclipse IDE or with certain Java versions it seems to capture `null`
// instead of the type variable
// instead of the type variable, see https://github.com/eclipse-jdt/eclipse.jdt.core/issues/975
"TypeToken captured `null` as type argument; probably a compiler / runtime bug");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public void write(JsonWriter out, DummyClass value) throws IOException {
static class Factory implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
@SuppressWarnings("unchecked") // the code below is not type safe, but does not matter for this test
@SuppressWarnings("unchecked") // the code below is not type-safe, but does not matter for this test
TypeAdapter<T> r = (TypeAdapter<T>) new TypeAdapter<DummyClass>() {
@Override
public DummyClass read(JsonReader in) throws IOException {
Expand Down

0 comments on commit 9bc4255

Please sign in to comment.