Всякий раз, когда я использую Map.Entry::getKey
в своих потоках для своих общедоступных методов, я получаю сообщение о том, что мой метод не является static
. Я даже пытался сделать свой метод static
, и он не работал.
Ниже приведена компиляция ошибка, которую я получаю от использования Map.Entry()::getKey()
:
Non-static method cannot be referenced from a static context
Мой код
/***
* Merge 2 Maps and add the values together if they are in both maps
*
* firstMap = {"Eggs": 3, "Cereal": 1}
* secondMap = {"Eggs": 10, "Coke": 23, "Cereal": 1}
*
* Answer = {"Eggs": 13, "Coke": 23, "Cereal": 2}
* Notice that the Eggs are now 13
*
* @param firstMap
* @param secondMap
* @return
*/
public Map<String, Integer> mergeAndAddValues(Map<String, String> firstMap, Map<String, String> secondMap) {
return Stream.of(firstMap, secondMap)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry()::getKey,
Map.Entry::getValue,
Integer::sum,
HashMap::new));
}
@tgdavies Правильно, но только с этим исправлением это не сработает. Тип элементов потока и тип возвращаемого значения несовместимы.
Вы можете использовать Map.Entry::getKey
и Map.Entry::getValue
, но ошибка вызвана чем-то другим. Map.Entry::getValue
и Integer::sum
возвращают разные типы.
Определение метода toMap
объясняет, почему:
public static <T, K, U, M extends Map<K, U>> Collector<T, ?, M> toMap(
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapFactory);
В определении можно увидеть, что:
valueMapper
имеет ? extends U
типmergeFunction
имеет U
типМеняем тип firstMap и secondMap с Map<String, String>
на Map<String, Integer>
ошибок нет. Это связано с тем, что тип, возвращаемый Map.Entry::getValue
, меняется с String
на Integer
.
Вот окончательный результат:
public Map<String, Integer> mergeAndAddValues(Map<String, Integer> firstMap, Map<String, Integer> secondMap) {
return Stream.of(firstMap, secondMap)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, Integer::sum, HashMap::new));
}
}
Аргументы метода имеют тип Map<String, String>
, а возвращаемый тип — Map<String, Integer>
.
Чтобы исправить ваш метод, вам нужно преобразовать записи типа Map.Entry<String, String>
в записиMap.Entry<String, Integer>
.
Это можно сделать, применив операцию map()
внутри вложенного потока. Статический метод Map.entry()
используется для создания нового вход на основе существующего. Я предполагаю, что все строки состоят только из цифр, иначе вам нужно применить дополнительную очистку перед их разбором.
public Map<String, Integer> mergeAndAddValues(Map<String, String> firstMap,
Map<String, String> secondMap) {
return Stream.of(firstMap, secondMap)
.flatMap(map -> map.entrySet().stream()
.map(entry -> Map.entry(entry.getKey(),
Integer.valueOf(entry.getValue()))))
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
Integer::sum));
}
Примечание разновидность Collectors.toMap
, которая не требует mapFactory
(т. е. версия Collectors.toMap
, которая принимает только три аргумента), используется, потому что по умолчанию вы получите HashMap
в результате.
Предоставление HashMap::new
вручную ничего вам не дает, наоборот ваш код становится немного более жестким, и если в будущем HashMap
будет заменен более производительной реализацией общего назначения Map
вы получите нет бесплатно, за это ваш код надо будет поменять.
I even tried making my method static ... I am getting from using Map.Entry()::getKey()
Non-static method cannot be referenced from a static context
Эта проблема не связана со статическим или экземплярным методом, к сожалению, это редкий случай, когда сообщение об ошибке не очень полезно. Если вы замените эти ссылки на методы лямбда-выражениями, то компилятор правильно укажет, что типы String
и Integer
несовместимы.
И не надо странных манипуляций с синтаксисом, очень советую ознакомиться с этими туториалами по лямбды и ссылки на методы.
И лямбда-выражения, и ссылки на методы используются для реализации функциональный интерфейс, который представляет собой интерфейс, определяющий один и только один абстрактный метод. т.е. оба лямбда и ссылка на метод должны реализовать этот одиночный метод.
Все аргументы, которые ожидает Collectors.toMap()
, являются встроенными в Java функциональные интерфейсы.
В случае, когда имплантация поведения, определенного функциональный интерфейс, уже существует (то есть у вас где-то есть метод, который делает то, что ожидается от метода, объявленного в интерфейсе.), вы можете использовать ее с ссылка на метод.
Есть четыре виды ссылок на методы (цитата из Учебник Оракулов, упомянутого выше):
ContainingClass::staticMethodName
containingObject::instanceMethodName
ContainingType::methodName
ClassName::new
Примечание, что синтаксис из ссылки на методы не требует круглые скобки ни после типа, ни после имени метода.
Так что правильный синтаксис будет Map.Entry::getKey
, и это ссылка на метод экземпляр произвольного объекта определенного типа. т.е. тип Map.Entry
на самом деле подразумевает элемент потока (объект, а не интерфейс).
Напоминание:Entry
— это вложенный интерфейс, представляющий пара ключ-значение, определенный внутри интерфейса Map
. Следовательно, правильный синтаксис для обращения к нему Map.Entry
(без скобок).
А getKey
— это один из методов экземпляра, объявленных интерфейсом Entry
.
Правильный синтаксис для определения Function
(который является стандартным функциональный интерфейс, ожидаемым в качестве первого аргументаCollectors.toMap()
с ссылка на метод:
Map.Entry::getKey
Где первая часть перед двойное двоеточие — это тип объекта, который будет передан в функция. И вторая часть — это имя метода.
Круглые скобки никогда используются после типа в Java (не путайте ссылку на тип с вызовом конструктора). И после имя метода круглые скобки опущены, потому что так был разработан язык, и я думаю, потому что ссылки на методы должны быть краткими и выразительными.
Map.Entry()::getKey должен быть Map.Entry::getKey