问题: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")
             ^

这是很容易犯的错误,注意到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时不能带括号。

你可能感兴趣的:(问题:Adaptation of argument list by inserting () has been deprecated)