Nested Wildcards

Nested wildcards

It is important to understand the implication of the wildcard types.

You already understood that you can assign your Map<Integer, Map<Integer, String>> to Map<?, ?> as Map<?, ?> implies arbitrary types, unknown to whoever might have a reference of the declared type Map<?, ?>. So you can assign any map to Map<?, ?>.

In contrast, if you have a Map<?, Map<?, ?>> it has an unknown key type but the value type is not unknown. It’s Map<?,?> the type, recall the information above, that can be assigned with any map.

So, the following code is legal:

Map<?, Map<?, ?>> map=new HashMap<>();
map.put(null, Collections.<String,String>singletonMap("foo", "bar"));
map.put(null, Collections.<Double,Integer>singletonMap(42.0, 1000));
map.put(null, Collections.<Object,Boolean>singletonMap(false, true));

Here, we are putting a null key as we can’t put anything else for keys but arbitrary typed maps as values as that’s what a value type of Map<?, ?> implies: can be assigned from arbitrary maps. Note that by iterating over the entries we can also set other entries having non-null keys to arbitrary maps then.

So I’m quite sure that you don’t want to assign your Map<Integer, Map<Integer, String>> to a Map<?, Map<?, ?>> and discover arbitrary maps not being Map<Integer, String> as values afterwards and that you are quite happy that the compiler doesn’t allow this.

What you actually want to do is to assign your map to a type which has both, key and value type, unknown but still telling that your values are maps:

Map<Integer, Map<Integer, String>> someMap = new HashMap<>();
Map<?, ? extends Map<?, ?>> map=someMap;

In the generic type system Map<Integer, String> is a sub-type of Map<?, ?> so you can assign it to Map<?, ?> as well as ? extends Map<?, ?>. This sub-type relationship is not different than the relationship of String to Object. You can assign any String to a variable of type Object but if you have a Map<?,String> you can’t assign it to Map<?,Object> but only to Map<?, ? extends Object> for the same reason: the map shall continue to contain Strings as values rather than receiving arbitrary objects.

Note that you can workaround this limitation. You can say:

Map<Integer, Map<Integer, String>> someMap = new HashMap<>();
Map<?, Map<?, ?>> map=Collections.unmodifiableMap(someMap);

Since the map returned by unmodifiableMap does not allow any modifications, it allows widening the key and value types. The contained values are of the specified type (i.e. Map<?, ?>) when you query the map, but attempts to put in arbitrary map values, while not rejected by the compiler, will be rejected at runtime.

Java generics, nested collection of wildcard

Because it would break type safety:

List<List<Object>> lo = new ArrayList<List<Object>>();
List<List<? extends Object>> ll = lo;
List<String> ls = new ArrayList<String>();
ll.add(ls);
lo.get(0).add(new Object());
String s = ls.get(0); // assigns a plain Object instance to a String reference

Double wildcard parameterization (nested wildcards) with Java generics

Use the following instead:

Set<? extends Class<?>> subTypes = reflections.getSubTypesOf(type);

The problem is that nested wildcards don't perform type capture. Declaring a Set<Class<?>> means "a set of classes of any type", while what's being returned is a Set<Class<? extends capture#19-of ?>>, which means "a set of classes of any type extending from some specific unknown type". In this case, that "specific unknown type" is derived from the type argument to T, which is inferred from type to be an unbounded wildcard capture (the ? in Class<?>).

For example, pretend that "specific unknown type" is Number:

Class<Number> type = ...
Set<Class<?>> subTypes = reflections.getSubTypesOf(type);

Here, getSubTypesOf returns a Set<Class<? extends Number>>. That type is not assignable to Set<Class<?>> because generic types aren't covariant. But, wildcard capture helps us express covariance, allowing types like Set<? extends Class<?>>. The caveat is that we can't add anything but null to such a set, since we don't know its specific type.

Related posts:

  • Java Generic List<List<? extends Number>>
  • Multiple wildcards on a generic methods makes Java compiler (and me!) very confused
  • What is PECS (Producer Extends Consumer Super)?

Similar posts:

  • Java: Wildcard Types Mismatch Results in Compilation Error
  • Issue with declaration of Map<String,Class<? extends Serializable>>
  • Bounded-wildcard related compiler error

Nested generics and wildcards

? means "unknown type". Since the type is unknown, the compiler can't guarantee that the type is Map<String, String>. So it refuses to let you add anything in the queue, since it could compromise its type-safety.

Your variable should be declared as

Queue<Map<String, String>> q = new LinkedList<Map<String, String>>();

or simply, if you're on Java 7 or later

Queue<Map<String, String>> q = new LinkedList<>();

Mixing nested type parameters and wildcards in Java

The reason is that the ? in List<?> could be "anything", but a different "anything" in each Map entry. That is, it would accept a List<String> in one entry, and a List<Integer> in another.

But you are passing in a Map that has the same type of List in every entry, so the type is not bound in the same way or the to same degree for freedom.

The "fix" is to lock the type to a specific type, but still being "anything" - just the same "anything* in every entry, by typing the method:

public static <T> void accept(Map<String, List<T>> multiMap) // complies

or if your method really doesn't need to know which type, use a wildcard to wrap the type:

public static void accept(Map<String, ? extends List<?>> multiMap) // compiles

This last version works because the type of the list, although being a wildcard, is fixed to an unknown, but consistent, type when called.



I find the typed version easier to read (and code), and the type is there for use should you decide later that your method needs to know the type.

Terms aggregation with nested wildcard path

Tdlr;

A bit late to the party.
I would suggest a different approach as I don't see a possible solution using wildcards.

My solution would involve using the copy_to to create a field that you will be able to access using aggregation.

Solution

The idea is to create a field that will store the values of all your classifiers.
Which you can be doing aggregation on.

PUT /54198251/
{
"mappings": {
"properties": {
"classifiers": {
"type": "keyword"
},
"parent": {
"type": "nested",
"properties": {
"child": {
"type": "nested",
"properties": {
"classifier": {
"type": "keyword",
"copy_to": "classifiers"
}
}
},
"child2": {
"type": "nested",
"properties": {
"classifier": {
"type": "keyword",
"copy_to": "classifiers"
}
}
}
}
}
}
}
}

POST /54198251/_doc
{
"parent": {
"child": {
"classifier": "c1"
},
"child2": {
"classifier": "c2"
}
}
}

GET /54198251/_search
{
"aggs": {
"classifiers": {
"terms": {
"field": "classifiers",
"size": 10
}
}
}
}

Will give you:

  "aggregations": {
"classifiers": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "c1",
"doc_count": 1
},
{
"key": "c2",
"doc_count": 1
}
]
}
}

Wild card ? extends T throws incompatible types error for non-static nested class

The problem is that the compiler doesn't know that the type of LinkedList<? extends T> is the same type as Node<? extends T> - the two wildcard types could be different subtypes of T, so the compile error is appropriate.

The fix is to type the method, locking the subtype to the same subtype for both:

public <U extends T> void concat(LinkedList<U> otherList) {
// see below
}

This is the same as a wildcard <? extends T>, but it's a constant for the method invocation.

You need a little more generics fu inside the method too:

public <U extends T>  void concat(LinkedList<U> otherList) {
// Because you have an inner class, you must use this syntax
LinkedList<U>.Node<U> otherListHead = otherList.head;
// Create the local typed Node using the foreign Node's data
Node<T> node = new Node<>(otherList.head.item);
// Add node to this and repeat for all Nodes in the other list
}

The reason it works without special syntax when Node is a static class, ie:

public <U extends T>  void concat(LinkedList<U> otherList) {
Node<U> otherListHead = otherList.head; // doesn't need LinkedList<U> prefix
// ...
}

is because whereas instances of static classes exist independently, inner classes can't exist without the enclosing class instance and this is just the syntax that java requires.

Incidentally, when the Node class is not static, the type T of Node is hiding the type T of LinkedList, so you should delete the type from Node, instead just using the type T from LinkedList which the inner class Node acquires from the enclosing LinkedList instance, ie:

public static class LinkedList<T> {
Node head;

private class Node { // non static version should not be typed
T item; // T comes from the enclosing class instance
Node next;

Node(T item){
this.item = item;
}
}
// ...
}

Perhaps that is what you intended.



Related Topics



Leave a reply



Submit