Как избавиться от предупреждения о безопасности нулевого типа в eclipse neon для TreeMap

Как избавиться от предупреждения в этом примере кода.

Я использую Eclipse Neon с Java 1.8 и org.eclipse.jdt.annotation_2.1.0.

import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

@NonNullByDefault
public class NullAnnotationTest4 {

    public static void main(String[] args) {

        final TreeMap<@Nullable Integer, @Nullable String> treeMap = new TreeMap<>();

        treeMap.put(3,  "Test1");
        treeMap.put(null, null);

        //This produces the warning
        final Set<@Nullable Entry<@Nullable Integer, @Nullable String>> set = treeMap.entrySet();

        for (final Iterator<@Nullable Entry<@Nullable Integer, @Nullable String>> it = set.iterator(); it.hasNext(); ) {

            final Entry<@Nullable Integer, @Nullable String> entry = it.next();

            if (entry != null && entry.getKey() == null && entry.getValue() != null)
                System.out.println(entry.getKey()+" is mapped to "+entry.getValue());
        }

    }
}

Предупреждение:

Null type safety (type annotations): 
The expression of type 
'Set<Map.Entry<@Nullable Integer,@Nullable String>>' 
needs unchecked conversion to conform to 
'Set<Map.@Nullable Entry<@Nullable Integer,@Nullable String>>'

Я пробовал несколько комбинаций @Nullable и @NonNullable. Даже ? extends, как было предложено в подобном случае здесь: https://bugs.eclipse.org/bugs/show_bug.cgi?id=507779

Но предупреждение всегда только перемещается, но никогда не уходит полностью.

Обновление:

Я избавился от предупреждения, используя эту строку:

final Set<? extends @Nullable Entry<@Nullable Integer, @Nullable String>> set = treeMap.entrySet();

Но я совершенно потерян, почему. Мне кажется, что я обманываю валидатор, теряя трек или что-то в этом роде, и код действительно начинает становиться уродливым.

Полный новый код:

import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

@NonNullByDefault
public class NullAnnotationTest4 {

    public static void main(String[] args) {

        final TreeMap<@Nullable Integer, @Nullable String> treeMap = new TreeMap<>();

        treeMap.put(3,  "Test1");
        treeMap.put(null, null);


        final Set<? extends @Nullable Entry<@Nullable Integer, @Nullable String>> set = treeMap.entrySet();

        for (final Iterator<? extends @Nullable Entry<@Nullable Integer, @Nullable String>> it = set.iterator(); it.hasNext(); ) {

            final Entry<@Nullable Integer, @Nullable String> entry = it.next();

            if (entry != null && entry.getKey() == null && entry.getValue() != null)
                System.out.println(entry.getKey()+" is mapped to "+entry.getValue());
        }

    }
}

Обновление 2 Коротышка вставил неверный код.


person Torge    schedule 22.11.2016    source источник
comment
Разве переменная set не должна быть просто Set<Entry<@Nullable Integer, @Nullable String>>? Записи в наборе записей, вероятно, никогда не должны быть нулевыми.   -  person Jiri Tousek    schedule 22.11.2016
comment
Я попробовал это первым. Но тогда ошибка в том, что он не может преобразовать Set<Map.Entry... в Set<Map.@NonNull Entry.. из-за @NonNullByDefault   -  person Torge    schedule 22.11.2016


Ответы (1)


По своей сути такие типы вопросов, как Set<@NonNull X> и Set<@Nullable X>, несовместимы: ни один из них не может быть назначен другому.

А именно: если у вас есть Set<@NonNull X>, клиенты ожидают извлечь ненулевые элементы и сломаются, если набор на самом деле Set<@Nullable X>. И наоборот, если у вас есть Set<@Nullable X>, клиенты ожидают, что смогут вставить null в набор, и сломаются, если набор на самом деле Set<@NonNull X>.

Эти соображения уместны всякий раз, когда вам приходится иметь дело с «устаревшим» типом Set<X>, о котором у нас недостаточно знаний: он может быть предназначен как Set<@NonNull X> или как Set<@Nullable X>. Проверка типов должна учитывать обе возможности (клиенты могут полагаться на любое предположение, потому что, например, так может сказать javadoc).

Как правило, в Java проблема чтения и записи решается с помощью ограниченных подстановочных знаков: Set<? extends X> гарантирует, что доступ для чтения всегда будет давать «как минимум» X (или лучше). Set<? super X> гарантирует, что требование для вставки новых элементов равно «максимум» X, т. е. любой X или выше будет принят в набор (какими бы ни были реальные требования фактического списка).

Чтобы применить вышеизложенное к нулевым аннотациям, просто скажите Set<? extends @Nullable X>, чтобы принять устаревший набор и поддерживать чтение из набора (значения будут не менее @Nullable X, а может быть и лучше, например, @NonNull X). Если вам нужно вставить в устаревший набор, назначьте его переменной типа Set<? super @NonNull X>. Это сообщает программе проверки типов, что @NonNull X всегда будет достаточно для вставки.

Вот почему Set<? extends @Nullable Entry<..> принимает результат treeMap.entrySet(), который на самом деле имеет тип Set<Entry<@Nullable Integer, @Nullable String>>. Здесь аргументы внутреннего типа полностью аннотированы из объявления treeMap, только аргумент типа верхнего уровня Entry не указан из-за устаревшей подписи entrySet().

Последнее упоминание также намекает на «настоящее» решение для примера: используйте внешние аннотации, чтобы указать, что entrySet() фактически возвращает @NonNull Set<@NonNull Entry<K,V>>. При этом никакая магия подстановочных знаков не требуется.

person Stephan Herrmann    schedule 24.11.2016