Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type safety for readValue() with TypeReference #2196

Closed
wants to merge 1 commit into from

Conversation

nguyenfilip
Copy link

In Java the type safety is enforced at run-time for reifiable types (e.g. arrays that throw ArrayStoreException) but for non-reifiable types such as generic collections, it is important that type safety is enforced at compile time. Its not possible to enforce type safety at runtime, because of the type erasure.

The ObjectMapper.readValue(String, TypeReference) was not type-safe at
compile time, because the type of parameter valueTypeRef was missing
generic type parameter T. This fact could cause heap pollution when
client would write something like this:

List<String> list;
list = mapper.readValue("[33]", new TypeReference<List<Integer>>(){});
String str = list.get(0); // throws ClassCastException

The ObjectMapper.readValue(String, TypeReference) was not type-safe at
compile time, because the type of parameter valueTypeRef was missing
generic type parameter T. This fact could cause heap pollution when
client would write something like this:

```
List<String> list;
list = mapper.readValue("[33]", new TypeReference<List<Integer>>(){});
String str = list.get(0); // throws ClassCastException
```
@cowtowncoder cowtowncoder changed the title Typesafety for readValue Type safety for readValue() with TypeReference Dec 8, 2018
@cowtowncoder
Copy link
Member

Ah. Yes, this seems reasonable.

My main/only concern is regarding compatibility with existing code. Due to type-erasure, I think change is binary-compatible, so not being problematic for things already compiled.
But I think it may be (slightly) source-incompatible, meaning stricter checking (which is a feature, to help type match) could expose problems and prevent compilation.

So, this probably should not go in 2.9.x (or earlier) patch release. It would be fine for 3.x (master), but it probably would be ok for 2.10 as well.

@cowtowncoder
Copy link
Member

@nguyenfilip Thank you again for submitting this -- I ended up manually merging these changes.

@@ -1453,7 +1453,7 @@ public JsonParser treeAsTokens(TreeNode n) {
* expected for result type (or has other mismatch issues)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public <T> T readValue(String content, TypeReference valueTypeRef)
public <T> T readValue(String content, TypeReference<T> valueTypeRef)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cowtowncoder just running in to this during an upgrade from 2.9 to 2.10

The purpose of the types on TypeReference is to supply concrete implementation classes through the reflection API, but often those implementations are not what you would want to return out of a method.

The purpose of on readValue is to define the left hand side of the assignment.

As a concrete example, say you have interface Foo and data class FooImpl. I would need to construct a TypeReference<List<FooImpl>> to supply appropriate type info to the objectMapper, but as the assigned type I would like to use List<Foo>.

In the previous case this worked due to the noted lack of type safety between return type and TypeReference type.
To get around this now I'd have to erase the type off the return value from readValue and manually cast back to List (or some other set of operations that has equally bad type safety to the prior untyped valueTypeRef case).

I don't have a suggestion, just an observation that this adds a bit more friction to the end user..

Maybe someone better at generics can figure out the proper incantation where the return value is type compatible with the valueType but still allows returning a less specific type than what is needed for deserialization. (Maybe that is not possible with java generics in this use case)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is unfortunately problematic. Had I realized problems with inability to vary type parameters independently, I would not have made this change.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 at least post an example how folks can fix this, heck of an issue to run into during an upgrade in a point release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants