Can't Cast to to Unspecific Nested Type with Generics

Can't cast to to unspecific nested type with generics

Use the following:

Msg<? extends Value<?>> someMsg = strMsg;

The problem is that the ? in Msg<Value<?>> objMsg is NOT capable of capture conversion. It's not "a Msg of Value of some type. It's "a Msg of Value of ANY type".

This also explains why along with the declaration change, I've also renamed the variable to someMsg. The Value can't just be any Object. It must belong to some type (String in this example).


A more generic example

Let's consider a more generic example of a List<List<?>>. Analogously to the original scenario, a List<List<?>> can NOT capture-convert a List<List<Integer>>.

    List<List<Integer>> lolInt = null;

List<List<?>> lolAnything = lolInt; // DOES NOT COMPILE!!!
// a list of "lists of anything"

List<? extends List<?>> lolSomething = lolInt; // compiles fine!
// a list of "lists of something"

Here's another way to look at it:

  • Java generics is type invariant
  • There's a conversion from Integer to Number, but a List<Integer> is not a List<Number>
    • Similarly, a List<Integer> can be capture-converted by a List<?>, but a List<List<Integer>> is not a List<List<?>>
  • Using bounded wildcard, a List<? extends Number> can capture-convert a List<Integer>
    • Similarly, a List<? extends List<?>> can capture-convert a List<List<Integer>>

The fact that some ? can capture and others can't also explains the following snippet:

    List<List<?>> lolAnything = new ArrayList<List<?>>(); // compiles fine!

List<?> listSomething = new ArrayList<?>(); // DOES NOT COMPILE!!!
// cannot instantiate wildcard type with new!

Related questions

  • Multiple wildcards on a generic methods makes Java compiler (and me!) very confused

    • Very long and detailed exploration into this problem
  • Java Generic List<List<? extends Number>>
  • Any simple way to explain why I cannot do List<Animal> animals = new ArrayList<Dog>()?
  • What is the difference between <E extends Number> and <Number>?

See also

  • Java Generics Tutorial

    • Generics and Subtyping | Wildcards | More Fun with Wildcards
  • Angelika Langer's Java Generics FAQ

    • What is a bounded wildcard?
    • Which super-subtype relationships exist among instantiations of generic types?

Calling java method with Set with nested ? generic type

Nested wildcards can cause some unexpected incompatibilities. Try this:

public String buildMessageForViolations(Set<? extends ConstraintViolation<?>> constraintViolations) {

multiple nested wildcard - arguments not applicable

The following compiles as expected:

    List<Set<? extends Set<?>>> list = new ArrayList<Set<? extends Set<?>>>();
list.add(new HashSet<Set<String>>());
list.add(new HashSet<Set<Integer>>());

The problem is that generics is type invariant.

Consider the simpler example:

  • Given that there is a casting conversion from Animal to Dog (e.g. Dog extends Animal)...

    • A List<Animal> IS NOT a List<Dog>
  • There is a capture conversion from List<? extends Animal> to a List<Dog>

Now here's what happens in this scenario:

  • Given that there is a capture conversion from Set<?> to Set<String>...

    • A Set<Set<?>> IS NOT a Set<Set<String>>
  • There is a capture conversion from Set<? extends Set<?>> to Set<Set<String>>

So if you want a List<T> where you can add a Set<Set<String>>, Set<Set<Integer>>, etc, then T is NOT Set<Set<?>>, but rather Set<? extends Set<?>>.

Related questions

  • Can't cast to to unspecific nested type with generics
  • Multiple wildcards on a generic methods makes Java compiler (and me!) very confused
  • Java Generic List<List<? extends Number>>
  • Any simple way to explain why I cannot do List<Animal> animals = new ArrayList<Dog>()?
  • What is the difference between <E extends Number> and <Number>?

See also

  • Java Generics Tutorial

    • Generics and Subtyping | Wildcards | More Fun with Wildcards
  • Angelika Langer's Java Generics FAQ

    • What is a bounded wildcard?
    • Which super-subtype relationships exist among instantiations of generic types?

Java generics incompatible types compilation error for generic static method call in Oracle Java SE 8u20 JDK

Anything between < & > is type invariant unless you use a ? wildcard.

The types of Set<? extends Object> & Set<Object> are different, and therefore need type variance to be considered compatible.

If they're at the top level, as for works1, the top-level Set is outside the < & >, so the normal Java type variance works, but for fails1, they're inside the < & >, so the types are invariant.

Type variance can be enabled within the < & > by changing the outermost part of the type declaration from Set<Set<...>> Set<? extends Set<...>>.

Thus, the following code compiles correctly:

Set<? extends Set<? extends Object>> works4 = Collections.<Set<Object>>emptySet();

I derived my answer from the answer to the following question (which was mentioned above by @MarkoTopolnik):

multiple nested wildcard - arguments not applicable

The answer to the following question provided a more detailed explanation:

Can't cast to to unspecific nested type with generics

Why is this conversion not valid?

It's not valid because if it was valid you'd be able to add non-integer lists to ml as well.

Example (not valid):

Map<String, List<Integer>> ml;
Map<String, List<?>> ml3 = ml;

ml3.put("strings", Arrays.asList("evil","string"));

List<Integer> l = ml.get("strings"); //see how this is going to fail?

Why is Map<String, ?> ml2 = ml; valid? That's because the use of a wildcard tells the compiler to not allow adding new elements, i.e. ml2.put("strings", Arrays.asList("evil","string")); would not be allowed (the compiler doesn't do a type check, it just sees the wildcard and knows you must not call that method.

Compilation failure calling method with nested wildcard

You should change your methods to the following:

private static <T, U extends Collection<?>> Stream<T> getStreamOfMappingToN(Map<T, U> map) {
return map.entrySet().stream().map(q -> q.getKey());
}

private static <T, U extends Collection<?>> Stream<T> getStreamOfMergedDistinctMappingToN(Map<String, Map<T, U>> map) {
return map.entrySet().stream()
.flatMap(p -> getStreamOfMappingToN(p.getValue())).distinct();
}

What changed is that instead of declaring the input type as ? extends Collection<U>, the method is accepting Map<T, U> but U is constrained to U extends Collection<?>.

This change represents what you really want to do: you are giving a Map as input. This map has two types T and U. T can be whatever type we give but U really needs to be a Collection of something, i.e. U must extends Collection<?>.


To find out why your example doesn't compile, please refer to this answer (or this one): wildcards only works on the 1st level, not deeper. As you found out, you need to add ? extends Map<...> to counter the fact that the wildcard was not applied recursively.

Still, I would recommend you use the first solution as I feel it gives cleaner code and the proper intent, instead of messing around with wildcards.

Java generics class cast exception

The problem lies here:

data = (Item[]) new Object[MAX_SIZE];

You are instantiating an array of Object and then you try to cast it as an array of Item, which throws an exception because Object does not extend your Item class, because it does not implement Comparable. What you would like instead is:

data = new Item[MAX_SIZE];

But you can't do this because Item is a generic type. If you want to create objects (or arrays of objects) of this type dynamically, you need to pass the Class object to your GD class's constructor:

import java.lang.reflect.Array;

public class GD<Item extends Comparable<Item>> {
private Item[] data;
private final int MAX_SIZE = 200;
public GD(Class<Item> clazz) {
data = (Item[]) Array.newInstance(clazz, MAX_SIZE);
}

public static void main(String[] args) {
GD<String> g = new GD<String>(String.class);
}
}

Why doesn’t MapString, SetString match MapT, Set??

You're missing the fact that Set<?> is also used as a generic parameter. And generics are invariant. i.e. when the parameter is Map<String, Set<?>>, the passed argument must be exactly Map<String, Set<?> (or a subtype of). Whereas with Set<V> the type argument is inferred.

You can solve this by using a bounded wildcard:

public <T> int numberOfValues(Map<T, ? extends Set<?>> map) {
...
}


Related Topics



Leave a reply



Submit