简体   繁体   English

正在为 Map.Entry::getKey 发出合并两个映射时的编译错误

[英]Compilation error while merging two Maps is being issued for Map.Entry::getKey

Whenever I use Map.Entry::getKey in my streams for my public methods, I get an issue around my method not being static .每当我在我的公共方法流中使用Map.Entry::getKey时,我都会遇到我的方法不是static的问题。 I even tried making my method static , and it didn't work.我什至尝试制作我的方法static ,但它没有用。

Below is the compile error I am getting from using Map.Entry()::getKey() :下面是我使用Map.Entry()::getKey()得到的编译错误

Non-static method cannot be referenced from a static context

My code我的代码

/***
 * 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));
}

You can use Map.Entry::getKey and Map.Entry::getValue but the error is caused by something else.您可以使用Map.Entry::getKeyMap.Entry::getValue但错误是由其他原因引起的。 Map.Entry::getValue and Integer::sum return different types. Map.Entry::getValueInteger::sum返回不同的类型。

The definition of toMap method explains why: 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);

In the definition you can see that:在定义中你可以看到:

  • valueMapper has ? extends U valueMapper? extends U ? extends U type ? extends U
  • mergeFunction has U type mergeFunction具有U类型

Changing the type of firstMap and secondMap from Map<String, String> to Map<String, Integer> , there are no errors.将 firstMap 和 secondMap 的类型从Map<String, String>更改为Map<String, Integer> ,没有错误。 This is because the type returned by Map.Entry::getValue changes from String to Integer .这是因为Map.Entry::getValue返回的类型从String更改为Integer

Here the final result:这里是最终结果:

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));
    }
}

The method arguments are of type Map<String, String> and the return type is Map<String, Integer> .方法 arguments 的类型为Map<String, String> ,返回类型为Map<String, Integer>

In order to fix your method, you need to coerce entries that are of type Map.Entry<String, String> into entries Map.Entry<String, Integer> .为了修复您的方法,您需要将类型为Map.Entry<String, String>条目强制转换为条目Map.Entry<String, Integer>

It could be done by applying map() operation inside the nested stream. Static method Map.entry() is used to create a new entry based on an existing one.这可以通过在嵌套的 stream 中应用map()操作来完成。 Static 方法Map.entry()用于基于现有条目创建新条目 I assume that all string are comprised of digits only, otherwise you need to apply an additional clearing before parsing them.我假设所有字符串仅由数字组成,否则您需要在解析它们之前应用额外的清除。

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));
}

Note a flavor of Collectors.toMap that doesn't require a mapFactory (ie a version of Collectors.toMap that accepts only three arguments) is used because you'll get a HashMap as a result by default.请注意,使用了一种不需要mapFactoryCollectors.toMap (即仅接受三个参数的Collectors.toMap版本),因为默认情况下您将获得HashMap作为结果。

Providing HashMap::new manually doesn't byes you anything, conversely your code becomes a bit more rigid, and if in the future HashMap will get replaced with a more performant general purpose implementation of the Map you will not get it for free, for that your code will need to be changed.手动提供HashMap::new不会给你带来任何麻烦,相反,你的代码会变得更加严格,如果将来HashMap将被Map的性能更高的通用实现所取代,你将不会免费获得它,因为您的代码需要更改。

I even tried making my method static... I am getting from using Map.Entry()::getKey()我什至尝试制作我的方法 static...我正在使用Map.Entry()::getKey()

Non-static method cannot be referenced from a static context无法从 static 上下文中引用非静态方法

This issue isn't connected with static or instance method, unfortunately it's a rare case when error message isn't very helpful.此问题与 static 或实例方法无关,不幸的是,错误消息不是很有帮助的情况很少见。 If you replace these method references with lambdas, then the compiler will correctly point that types String and Integer are incompatible.如果您将这些方法引用替换为 lambda,那么编译器将正确指出类型StringInteger是不兼容的。

And there's no need for strange manipulations with a syntax, I kindly advise you to get familiar with these tutorials on lambdas and method references .并且不需要使用语法进行奇怪的操作,我建议您熟悉这些关于lambdas方法引用的教程。

Both lambda expressions and method references are used to provide an implementation of a function interface , which is an interface that defines one and only one abstract method . lambda 表达式方法引用都用于提供function 接口的实现,该接口定义了一个且仅一个抽象方法 Ie both lambda and method reference should implement this single method .lambda方法引用都应该实现这个单一的方法

All arguments that Collectors.toMap() expects are functional interfaces built-in in Java. Collectors.toMap()期望的所有 arguments 都是 Java 中内置的功能接口

In the case when implantation of the behavior defined by a function interface already exists ( ie you have a method somewhere which does what the method declared in the interface is expected to do ), you can make use of it with a method reference .如果由function 接口定义的行为植入已经存在(即您在某个地方有一个方法执行接口中声明的方法预期执行的操作),您可以通过方法引用来使用它。

There are four kinds of method references (quote from the Oracles's tutorial referenced above):有四种方法引用(引用上面引用的Oracles 教程):

  • Reference to a static method ContainingClass::staticMethodName引用 static 方法ContainingClass::staticMethodName
  • Reference to an instance method of a particular object containingObject::instanceMethodName引用特定的实例方法 object containingObject::instanceMethodName
  • Reference to an instance method of an arbitrary object of a particular type ContainingType::methodName引用特定类型ContainingType::methodName的任意 object 的实例方法
  • Reference to a constructor ClassName::new引用构造函数ClassName::new

Note that the syntax of method references doesn't require parentheses , neither after a type nor after a method name.请注意方法引用语法不需要括号,无论是在类型之后还是在方法名称之后。

So correct syntax will be Map.Entry::getKey , and that is a reference to an instance method of an arbitrary object of a particular type .因此正确的语法将是Map.Entry::getKey ,这是对特定类型的任意 object 的实例方法的引用 Ie type Map.Entry actually implies an element of the stream (an object, not an interface).即键入Map.Entry实际上暗示了 stream 的一个元素(一个 object,不是接口)。

Reminder: Entry is a nested interface that represents a key-value pair , defined in inside a Map interface.提示: Entry是一个嵌套接口,表示一个键值对,定义在一个Map接口内部。 Hence, the correct syntax to refer to it Map.Entry (without any parentheses).因此,引用它的正确语法是Map.Entry (没有任何括号)。

And getKey is one of the instance methods declared by the Entry interface.getKeyEntry接口声明的实例方法之一。

Recap回顾

The correct syntax to define a Function ( which is standard functional interface expected as the first argument by the Collectors.toMap() with a method reference is:定义Function的正确语法(这是Collectors.toMap()预期作为第一个参数的标准功能接口方法引用是:

Map.Entry::getKey

Where the first part before the double colon is the type of the object that will be passed to a function .双冒号之前的第一部分是将传递给function的 object 的类型 And second part is a method name .第二部分是方法名称

Parentheses are never used after a type in Java (don't confuse the reference to a type with a constructor invocation). Java 中的类型之后永远不会使用括号(不要将对类型的引用与构造函数调用混淆)。 And after a method name parentheses are dropped because that how the language was designed, and I guess because method references are intended to be concise and expressive.方法名称之后去掉括号是因为语言是这样设计的,我猜是因为方法引用的目的是简洁和富有表现力。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM