2015-07-03 4 views
5

Я наткнулся на некоторые, по крайней мере для меня, странное поведение в Eclipse 4.4 и Java 8 build 45 при выполнении рефакторинга экспрессии. Следующий пример показывает оригинальную и без ошибок коды перед применением экстракта рефакторинга:Несоответствие типов после извлечения выражения с общим типом возвращаемого значения

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()) { 
     } 
    } 
} 

результата рефакторинга Eclipse, выглядит, как это и приводит к сообщению об ошибке ниже, который ссылается на доступ для чтения из entrySet в объявление цикла:

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> 

я изменил тип объявления entrySet к Set<Map.Entry<? extends K, ? extends V>>. На этот раз, ошибка отображается в инициализаторе декларации, говоря:

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>> 

Поскольку исходный код компилируется, я немного озадачен. Может быть, кто-то может мне помочь и дать объяснение? Заранее спасибо!

+1

Обратите внимание, что 'Set > entrySet = mapToCopy.entrySet(); 'будет работать. [JLS § 14.4.2] (http://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.14.2) говорит о переводе 'Iterable' в расширенном 'for'. Также см. [Этот ответ] (http://stackoverflow.com/a/16753901/5065475) –

+0

@AndyBrown: Можете ли вы подробнее остановиться на «Обратите внимание, что [...] будет работать», пожалуйста. Что мне нужно изменить, чтобы заставить его работать? – Marcus

+0

В java8 также попробуйте 'map.forEach ((ключ, значение) -> {...})'. ключ/значение выводятся на соответствующие типы, как неизвестный подтип K/V. API также достаточно гибкий, если мы диктуем типы - 'map.forEach ((К-ключ, значение V) -> {...})' – ZhongYu

ответ

0

Позволяет первый отзыв оригинальный источник:

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

внутри (и во время выполнения), это будет скомпилирована и будет работать как:

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

где ? extends K и ? extends V будут заменены некоторые реальные типы после стирания типа. Компилятор будет знать, какой тип этого типа, и не будет поднимать Exception для несовместимости типа.

С другой стороны, если вы реорганизовывать источник этого,

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) { 

    } 
} 

, то компилятор не будет иметь никаких доказательств того, что держит entrySet того же типа, как Map.Entry<? extends K, ? extends V>, просто потому, что подстановочные (?) всегда означает что-то неизвестно, т. е. нет гарантии, что подстановочный знак от значения ключа ввода entrySet будет таким же, как и значение ключа entry (из цикла). Не будучи на сто процентов уверены, что типы совместимы, компилятор создает ошибку времени компиляции **, хотя ** у вас может быть уверенность, что эти типы будут такими же во время выполнения.

+0

Благодарим вас за ответ. Почему ваш третий фрагмент кода показывает указанную ошибку в инструкции цикла? Я не могу воспроизвести это. В моем третьем фрагменте кода ошибка указана в объявлении. – Marcus

+0

Я думаю, проблема не связана с циклом for. Я подробно остановился на этом вопросе и начал новую тему [здесь] (http://stackoverflow.com/questions/32143844/type-mismatch-when-using-map-entryset). – Marcus

Смежные вопросы