问题(Adaptation|问题:Adaptation of argument list by inserting () has been deprecated)
在Scala中,可以将函数当做一个参数传入方法中调用。假设有如下一个方法:
def sayHelloBeforeAction(action:Unit=>Unit){
println("Hello!")
action()
}
该方法接受一个类型为
Unit=>Unit
的函数action
为参数,在方法中调用此函数:action()
。这看起来并没有什么问题,虽然也能编译通过,但是却会报告一个警告:warning: Adaptation of argument list by inserting () is deprecated: this is unlikely to be what you want.
signature: Function1.apply(v1: T1): R
given arguments:
after adaptation: Function1((): Unit)
action()
^
然而,如果
action
接受至少一个参数,则不会出现此警告,这是为什么呢?通过查阅StackOverflow的问题: Adaptation of argument list by inserting () has been deprecated,其高票答案给出了如下解释:
Unit
类型自动推断在Scala 2.11时就被废弃了,因为它可能引发一些令人疑惑的行为。考虑如下代码:
class Foo[T](value: T)
val x = new Foo
这显然无法通过编译,因为调用的构造方法需要一个类型为
T
的参数,但是却没有给出。然而令人惊讶的是,在scala 2.10.4之前这是可以编译通过的,没有任何错误和警告。这是因为编译器自动推断出了
Unit
类型的参数,所以它会将这段代码替换为:val x = new Foo[Unit](()) // Foo[Unit]
正如这个新引进的警告信息所言,这可能不是你想要的结果。
另一个比较出名的例子如下:
scala> List(1,2,3).toSet()
// res1: Boolean = false
调用
toSet()
本应该是一个编译时错误,因为toSet
是无参方法(译注:在Scala中如果定义不带括号的无参方法,那么调用时也不能加括号)。但是编译器会竭尽所能地使它通过编译,最终上面的代码被翻译成:scala> List(1,2,3).toSet.apply(())
这就意味着:检测
()
是否属于该集合。显然是否定的,所以上面的代码返回了res1: Boolean = false
。(译注:实际上,上面代码可以看作是两步:首先,val set=List(1,2,3).toSet
,这将得到:set: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
。并且,由于集合Set
类有一个apply
方法:def apply(elem: A)
,其作用是检测elem
是否属于该集合,所以set()
就被拓展成set.apply()
即:set.apply(())
。)所以,从Scala 2.11开始,如果要将
()
当做Unit
类型参数传入,就必须显式的写出。因此,由于
action
是Unit=>Unit
类型的,使用时不要写成action()
而应该显式地写成action(())
。下面一个问题是,如何使用这个
sayHelloBeforeAction
函数,考虑下面代码:sayHelloBeforeAction{
println("Tom")
}
报的错误是:
:14: error: type mismatch;
found: Unit
required: Unit => Unit
println("Tom")
^
如果改成:
sayHelloBeforeAction{
()=>println("Tom")
}
仍然会报错:
:14: error: type mismatch;
found: () => Unit
required: Unit => Unit
()=>println("Tom")
^
【问题(Adaptation|问题:Adaptation of argument list by inserting () has been deprecated)】这是很容易犯的错误,注意到
Unit
是类型,而()
是该类型的唯一值,这么写就好像是定义100=>println...
一样荒谬。实际上应该写成如下形式:
sayHelloBeforeAction{
x:Unit=>println("Tom")
}Hello!
Tom
然而,此处虽然指定了类型为
Unit
的参数x
,但是x
在这里显然多余了,可以使用占位符语法来简化:sayHelloBeforeAction{
_=>println("Tom")
}Hello!
Tom
但是话说回来,其实还有提升的空间。注意到在这里没有必要将
action
定义为Unit=>Unit
,这么定义使得它必须接受一个类型为Unit
的参数,从而action()
其实被推断为action(())
。因为action
不需要接受任何参数,不妨将其定义为传名参数:def sayHelloBeforeAction(action: =>Unit){
println("Hello!")
action
}sayHelloBeforeAction{
println("Tom")
}Hello!
Tom
注意在定义传名参数
action: =>Unit
时,冒号和=>
之间必须有空格。并且在内部使用action
时不能带括号。推荐阅读
- parallels|parallels desktop 解决网络初始化失败问题
- jhipster|jhipster 升级无效问题
- “精神病患者”的角度问题
- 解决SpringBoot引用别的模块无法注入的问题
- Hive常见问题汇总
- 姚老师互动问答会|姚老师互动问答会 # 问题001(如何更有智慧的和身边人分享金刚智慧())
- 【Hadoop踩雷】Mac下安装Hadoop3以及Java版本问题
- 【教育故事】|【教育故事】 一个“问题学生”的蜕变
- 蓝桥杯试题
- 记录iOS生成分享图片的一些问题,根据UIView生成固定尺寸的分享图片