Double wildcard error

I am trying to define an operator ++

for my custom type Map

like this:

@Override
public MutableMap<K, V> $plus$plus(Map<? extends K, ? extends V> map)
{
    HashMap<K, V> copy = this.copy();
    map.$plus$plus$eq(map);
    return copy;
}

      

The operator is ++=

defined as follows:

public void $plus$plus$eq(Map<? extends K, ? extends V> map);

      

However, the compiler complains about a line map.$plus$plus$eq(map);

with the following insane error:

The method $plus$plus$eq(Map<? extends capture#10-of ? extends K,
? extends capture#11-of ? extends V>) in the type
Map<capture#10-of ? extends K,capture#11-of ? extends V> is not
applicable for the arguments
(Map<capture#12-of ? extends K,capture#13-of ? extends V>)

      

As you can see in this screenshot, none of the solutions provided by Eclipse make sense yet:

Eclipse marker

I've been working with Java generics for a long time, even developing my own generic type system for user programming (whose library I'm currently trying to code), but I've never had this error before.


EDIT . Interestingly, choosing Map

for the original type (Map)

seems to fix the problem.

map.$plus$plus$eq((Map) map);

      

However, changing the cast to (Map<?, ?>)

(which is what the second Eclipse solution does) throws a similar error.

+3


source to share


2 answers


It works if you are using a raw type Map

, but you lose type safety. Don't use raw types. They only exist for compatibility with Java 1.4 and older, which did not have generics.

It doesn't work with wildcards because the compiler doesn't know which types the wildcards represent ?

. The reason is the same as for the question why you cannot call add()

onList<? extends T>

.

Note that the wildcard does not mean that you can use any object types where the key type extends K

and the value type continues V

. Instead, it means that you have a map where the keys are of a specific but unknown type K

and the values ​​are of a specific but unknown type V

. You cannot call $plus$plus$eq

on a map like this, because the compiler doesn't know the exact types, so it can't check for them.



Basically, the wildcards Map

you call the on method on can stand for types that are different from the wildcards Map

you pass as an argument - although you can see in this case that they must be the same, because in this case you are using the same object Map

.

You can fix this by using type parameters instead of wildcards:

@Override
public <KK extends K, VV extends V> MutableMap<K, V> $plus$plus(Map<KK, VV> map)
{
    HashMap<K, V> copy = this.copy();
    map.$plus$plus$eq(map);
    return copy;
}

      

+4


source


Here's a simplified example that doesn't compile either.

class Foo<T> {

    void bar(Foo<? extends T> foo) {
        foo.bar(foo);
    }
}

      



Suppose foo

u goo

are of type Foo<? extends T>

. This means that it foo

has a type Foo<U>

where it U

is a subtype T

and goo

has a type Foo<V>

where V

it is also a subtype T

. You wouldn't expect this foo.bar(goo)

to work because it V

might not be a subtype U

. For this reason foo.bar(foo)

it will not compile either. This seems crazy because foo

both foo

are the same instance, but whether the arguments apply or not depends only on their compile-time type.

+2


source







All Articles