Unit 的函数 action 为参数,。问题(Adaptation|问题:Adaptation of argument list by inserting () has been deprecated)。" />

问题(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类型参数传入,就必须显式的写出。
因此,由于actionUnit=>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时不能带括号。

    推荐阅读