Объявление Возврата функции Java Generics

#java #generics

Вопрос:

Я новичок в дженериках Java и мне удалось заставить getKeyByValue функцию работать с a HashMap<String, String> , но я не понимаю, как объявление функции может быть неоднозначным/избыточным и все еще работать. Например, оба эти объявления работают, но первое не имеет для меня большого смысла:

     private <T, E> String getKeyByValue(Map<String, E> map, String value) {
        for (Entry<String, E> entry : map.entrySet()) {
            if (value.equals(entry.getValue())) {
                return entry.getKey();
            }
        }
        return null;
    }
 

Пример с просто <E> :

     private <E> String getKeyByValue(Map<String, E> map, String value) {
        for (Entry<String, E> entry : map.entrySet()) {
            if (value.equals(entry.getValue())) {
                return entry.getKey();
            }
        }
        return null;
    }
 

Комментарии:

1. Я не понимаю. Вас беспокоит неиспользованная T декларация?

2. ДА. Я не знал, что вы можете указывать избыточные параметры.

3. Даже E в значительной степени излишне. String.equals(E) не вернет значение true, если E объект не является a String . Поэтому map можно просто объявить как Map<String, String> , и от всех переменных типа можно избавиться.

4. да, этот код разбит, так как value.equals(….) сравнивает тип E со строкой

Ответ №1:

Для первого примера тип общего формата T не используется, следовательно, его можно удалить, что приводит к тому, что реализация будет такой же, как и реализация второго метода.

Во второй реализации генераторы Java используются без утилиты, так как equals метод (вы вызываете с типизированным параметром E ) для java.lang.String класса принимает Object аргумент, следовательно, любой объект поместился бы там.

 private <E> String getKeyByValue(Map<String, E> map, String value) {
    for (Map.Entry<String, E> entry : map.entrySet()) {
        if (value.equals(entry.getValue())) { // there is no type checking needed over the value as it can be any acceptable object
            return entry.getKey();
        }
    }
    return null;
}
 

Тот же метод может быть переписан с использованием ? подстановочного знака в качестве формального типа, поскольку над значениями не требуются операции, ограниченные типом Map :

 private String getKeyByValue(Map<String, ?> map, String value) {
    for (Map.Entry<String, ?> entry : map.entrySet()) {
        if (value.equals(entry.getValue())) {
            return entry.getKey();
        }
    }
    return null;
}
 

Ответ №2:

Ваш первый пример — это просто объявление параметров типа, которые никогда не используются-вы можете объявлять их сколько угодно!

 private <T, E, F, G> String getKeyByValue(Map<String, E> map, String value) {
    for (Entry<String, E> entry : map.entrySet()) {
        if (value.equals(entry.getValue())) {
            return entry.getKey();
        }
    }
    return null;
}
 

Комментарии:

1. А как насчет Map<String, E> map «и Map.Entry#getKey «?

2. запись.getKey возвращает тип строки, вы хотели вернуть тип значения?

Ответ №3:

В первом случае параметр типа T явно избыточен.

С другой точки зрения, аналогичным случаем является объявление входного параметра метода без его использования. Вы можете подумать, что компилятор не должен позволять нам этого делать. Но иногда такая «гибкость» может быть полезной. Например, у нас может быть родительский класс, имеющий метод с входным параметром, используемым только в дочернем классе. Хотя я не могу придумать сценарий, в котором параметр избыточного типа был бы полезен, кто-то может использовать такую гибкость языка для решения своей проблемы.

Я бы сказал, что первый случай следует рассматривать как запах кода, точно так же, как пустой оператор if или объявление неиспользуемой переменной. IDE и linter-это правильный инструмент для их решения, а не компилятор.