Type mismatch after extraction of expression with common return type

I came across some, at least for me, strange behavior in Eclipse 4.4 and Java 8 build 45 when performing expression refactoring. The following example shows the source code and the error-free code before applying the extract refactoring:

import java.util.Map; import java.util.Set; public class MyMap<K, V> { public void putAll(final Map<? extends K, ? extends V> mapToCopy) { for (Map.Entry<? extends K, ? extends V> entry : mapToCopy.entrySet()) { } } } 

The result of the Eclipse refactoring looks like this and leads to the error message below, which is related to the reading type entrySet in the loop declaration:

  public void putAll(final Map<? extends K, ? extends V> mapToCopy) { Set<?> entrySet = mapToCopy.entrySet(); for (Map.Entry<? extends K, ? extends V> entry : entrySet) { ^^^^^^^^ } } Type mismatch: cannot convert from element type capture#3-of ? to Map.Entry<? extends K,? extends V> 

I changed the entrySet declaration entrySet to Set<Map.Entry<? extends K, ? extends V>> Set<Map.Entry<? extends K, ? extends V>> Set<Map.Entry<? extends K, ? extends V>> . This time the error is indicated in the declaration initializer:

  public void putAll(final Map<? extends K, ? extends V> mapToCopy) { Set<Map.Entry<? extends K, ? extends V>> entrySet = mapToCopy.entrySet(); ^^^^^^^^^^^^^^^^^^^^ for (Map.Entry<? extends K, ? extends V> entry : entrySet) { } } Type mismatch: cannot convert from Set<Map.Entry<capture#1-of ? extends K,capture#2-of ? extends V>> to Set<Map.Entry<? extends K,? extends V>> 

Since the source code is compiling, I'm a bit puzzled. Maybe someone can help me and give an explanation? Thanks in advance!

+5
source share
1 answer

Allows you to first view the source:

 public void putAll(final Map<? extends K, ? extends V> mapToCopy) { for (Map.Entry<? extends K, ? extends V> entry : mapToCopy.entrySet()) { } } 

Internally (and in Runtime), this will compile and work like:

 public void putAll(final Map mapToCopy) { for (Iterator<Map.Entry> iterator = mapToCopy.iterator; iterator.hasNext();) { } } 

where ? extends K ? extends K and ? extends V ? extends V will be replaced with some real types after type erasure. The compiler will know what type is of this type and will not raise an Exception for type incompatibility.

On the other hand, if you reorganize the source for this,

 public void putAll(final Map<? extends K, ? extends V> mapToCopy) { Set<Entry<? extends K, ? extends V>> entrySet = mapToCopy.entrySet(); ^^^^^^^^ for (Map.Entry<? extends K, ? extends V> entry : entrySet) { } } 

then the compiler will have no evidence that the entrySet is of the same type as Map.Entry<? extends K, ? extends V> Map.Entry<? extends K, ? extends V> Map.Entry<? extends K, ? extends V> , simply because the wildcard ( ? ) always means something is unknown , that is, there is no guarantee that the wildcard from the value of the entrySet key will be the same as the value of the entry key (from the loop). Not being 100% sure that the types are compatible, the compiler generates a compile-time error **, although ** you can be sure that these types will be the same at runtime.

0
source

All Articles