合并两个地图时为 Map.Entry::getKey 发出编译错误
每当我在我的公共方法的流中使用 Map.Entry::getKey
时,我都会遇到一个关于我的方法不是静态
的问题。我什至尝试将我的方法设置为静态
,但它不起作用。
下面是我使用 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));
}
Whenever I use Map.Entry::getKey
in my streams for my public methods, I get an issue around my method not being static
. I even tried making my method static
, and it didn't work.
Below is the compile error I am getting from using 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));
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
方法参数的类型为
Map
,返回类型为Map
。为了修复您的方法,您需要将
Map.Entry
类型的条目强制转换为条目Map.Entry<字符串,整数>
。这可以通过在嵌套流内应用
map()
操作来完成。静态方法Map.entry()
用于基于现有条目创建一个新条目。我假设所有字符串仅由数字组成,否则您需要在解析它们之前应用额外的清除。注意
Collectors.toMap
的一种风格,不需要mapFactory
(即Collectors.toMap
的版本)使用仅接受三个参数)是因为默认情况下您将获得一个HashMap
作为结果。手动提供
HashMap::new
不会给你带来任何好处,相反,你的代码会变得更加僵化,并且如果将来HashMap
将被更高性能的通用代码取代Map
的目的实现,您将不能免费获得它,因为您的代码需要更改。此问题与静态或实例方法无关,不幸的是,这是一种罕见的情况当错误消息不是很有帮助时。如果您用 lambda 替换这些方法引用,则编译器将正确指出类型
String
和Integer
不兼容。并且不需要使用语法进行奇怪的操作,我建议您熟悉 lambda 和 方法引用。
lambda 表达式和方法引用都用于提供函数接口的实现,该接口定义了一个且仅一个 >抽象方法。即lambda和方法引用都应该实现这个单一方法。
Collectors.toMap()
期望的所有参数都是 Java 中内置的函数接口。当由函数接口定义的行为植入已经存在的情况下(即您在某处有一个方法可以执行接口中声明的方法预期执行的操作) ,您可以通过方法引用来使用它。
有四种方法引用(引自上面引用的Oracles教程):
ContainingClass::staticMethodName
containingObject::instanceMethodName
ContainingType::methodName
ClassName::new
请注意,方法引用的语法不需要括号 >,既不在类型之后,也不在方法名称之后。
因此,正确的语法是
Map.Entry::getKey
,这是对特定类型的任意对象的实例方法的引用 。即Map.Entry
类型实际上意味着流的一个元素(一个对象,而不是一个接口)。提醒:
Entry
是一个嵌套接口,表示键值对,在内部定义地图接口。因此,引用它的正确语法是
Map.Entry
(不带任何括号)。getKey
是Entry
接口声明的实例方法之一。回顾一下
定义
函数
的正确语法(这是标准函数接口,预期作为第一个参数Collectors.toMap()
使用方法引用是:双冒号之前的第一部分是将传递给的对象的类型 >函数。第二部分是方法名称。
在 Java 中,从不使用括号(不要混淆对的引用)。带有构造函数调用的类型)。在方法名称之后。括号被删除是因为语言的设计方式,我猜是因为方法引用旨在简洁和富有表现力。
The method arguments are of type
Map<String, String>
and the return type isMap<String, Integer>
.In order to fix your method, you need to coerce entries that are of type
Map.Entry<String, String>
into entriesMap.Entry<String, Integer>
.It could be done by applying
map()
operation inside the nested stream. Static methodMap.entry()
is used to create a new entry based on an existing one. I assume that all string are comprised of digits only, otherwise you need to apply an additional clearing before parsing them.Note a flavor of
Collectors.toMap
that doesn't require amapFactory
(i.e. a version ofCollectors.toMap
that accepts only three arguments) is used because you'll get aHashMap
as a result by default.Providing
HashMap::new
manually doesn't byes you anything, conversely your code becomes a bit more rigid, and if in the futureHashMap
will get replaced with a more performant general purpose implementation of theMap
you will not get it for free, for that your code will need to be changed.This issue isn't connected with static or instance method, unfortunately it's a rare case when error message isn't very helpful. If you replace these method references with lambdas, then the compiler will correctly point that types
String
andInteger
are incompatible.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.
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. I.e. both lambda and method reference should implement this single method.
All arguments that
Collectors.toMap()
expects are functional interfaces built-in in Java.In the case when implantation of the behavior defined by a function interface already exists (i.e. 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.
There are four kinds of method references (quote from the Oracles's tutorial referenced above):
ContainingClass::staticMethodName
containingObject::instanceMethodName
ContainingType::methodName
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. I.e. typeMap.Entry
actually implies an element of the stream (an object, not an interface).Reminder:
Entry
is a nested interface that represents a key-value pair, defined in inside aMap
interface. Hence, the correct syntax to refer to itMap.Entry
(without any parentheses).And
getKey
is one of the instance methods declared by theEntry
interface.Recap
The correct syntax to define a
Function
(which is standard functional interface expected as the first argument by theCollectors.toMap()
with a method reference is:Where the first part before the double colon is the type of the object that will be passed to a function. 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). 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.
您可以使用
Map.Entry::getKey
和Map.Entry::getValue
但错误是由其他原因引起的。Map.Entry::getValue
和Integer::sum
返回不同的类型。toMap
方法的定义解释了原因:在定义中你可以看到:
valueMapper
has? extends U
typemergeFunction
hasU
type将firstMap和secondMap的类型从
Map
更改为Map
,没有错误。这是因为Map.Entry::getValue
返回的类型从String
更改为Integer
。这里是最终结果:
You can use
Map.Entry::getKey
andMap.Entry::getValue
but the error is caused by something else.Map.Entry::getValue
andInteger::sum
return different types.The definition of
toMap
method explains why:In the definition you can see that:
valueMapper
has? extends U
typemergeFunction
hasU
typeChanging the type of firstMap and secondMap from
Map<String, String>
toMap<String, Integer>
, there are no errors. This is because the type returned byMap.Entry::getValue
changes fromString
toInteger
.Here the final result: