0

I have the following code:

String test = "[{\"color\":\"red\"}]";
Class<? extends Base> baseObject = Base.class;
Collection<? extends Base> elements = new ArrayList<Base>();
 if (test.startsWith("[")) {
    elements.addAll(new ObjectMapper().readValue(test, Collection.class));
 } else {
   elements.add(new ObjectMapper().readValue(test, baseObject));
}

However I get on

elements.addAll(new ObjectMapper().readValue(test, Collection.class));

a compilation warning:

The expression of type Collection needs unchecked conversion to conform to Collection<? extends capture#1-of ? extends Base>

and for elements.add(new ObjectMapper().readValue(test, baseObject));

a compilation error:

The method add(capture#2-of ? extends Base) in the type Collection<capture#2-of ? extends Base> is not applicable for the arguments (capture#3-of ? extends Base)

What is wrong?

9
  • 1
    Why not simply Collection<Base> elements = new ArrayList<Base>();? Commented Jun 11, 2013 at 14:56
  • 2
    String test = "[{"color":"red}]"; <-- i think you forgot to escape the quotes Commented Jun 11, 2013 at 14:57
  • Did you paste that code directly? Because String test = "[{"color":"red}]"; doesn't compile because you didn't escape the quotes: String test = "[{\"color\":\"red}]"; Commented Jun 11, 2013 at 14:57
  • @Brian - sure - sorry about it - ONly the string I wrote by hand. I will fix it Commented Jun 11, 2013 at 14:58
  • 1
    See my answer here Commented Jun 11, 2013 at 14:59

2 Answers 2

2

Error can be easily explained. Your collection is defined to hold instances of classes that extends Base. ObjectMapper.readValue is defined as following:

public <T> T readValue(JsonParser jp, Class<T> valueType)

This means that it returns instance of class specified as a second argument. If second argument is Collection.class this method returns Collection. Just Collection, not Collection<? extends Base> and not Collection<Base>. So, java compiler cannot be sure that you are going to put collection that contains correct objects into elements defined as Collection<? extends Base>. Moreover java syntax does not allow to supply as a parameter class with generics, i.e. you cannot call readValue(c, Collection<Base>.class).

The second case is more complicated. baseObject is defined as Class<? extends Base>. elements collection is defined as Class<? extends Base> too. So, what's the problem. The problem is in ?. The question mark means "something that extends Base". But these are 2 different "something" in both cases.

The solution can be either change definition of collection to Class<Base> or to use one class or method generic parameter in both cases, e.g.:

public <T extends Base> myMethod() {
    Class<T> baseObject = Base.class;
    Collection<T> elements = new ArrayList<T>();
    elements.add(new ObjectMapper().readValue(test, baseObject));
}

Now both baseObject and elements use the same type T that indeed extends Base.

Sign up to request clarification or add additional context in comments.

Comments

1

You can't add things to a collection with a wildcard type (that doesn't have an explicit lower bound using super, according to Brian; that does make some sense, as a subclass can always be cast to one of its superclasses, but I haven't used super-bounded wildcards much before so I'm not too sure of the details).

http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html

Specifically:

Since we don't know what the element type of c stands for, we cannot add objects to it. The add() method takes arguments of type E, the element type of the collection. When the actual type parameter is ?, it stands for some unknown type. Any parameter we pass to add would have to be a subtype of this unknown type. Since we don't know what type that is, we cannot pass anything in. The sole exception is null, which is a member of every type.

Type erasure is a... well, you know.

5 Comments

Not quite. You can add things to a collection that uses super, e.g. you can add any subclass of InputStream to a Collection<? super InputStream>
I still get a compilation warning for elements.addAll(new ObjectMapper().readValue(jsonRep, Collection.class));
@Odelya Conversions between generic types and their raw versions always yield warnings about unchecked conversions. It's more of a message from the compiler that what you're doing could have issues, but it can't know until runtime if there actually will be any. (Or something like that.) You can ignore the warning with @SuppressWarnings("unchecked").
I added the suppresswarning - but isn't a better way to do it? otherwise - does the compiler add it?
@Odelya If my guess that you're parsing JSON (going by your test string) is correct, a better way to do things would be to use an existing parser. eclipsesource.com/blogs/2013/04/18/minimal-json-parser-for-java

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.