美文网首页
问题:Adaptation of argument list b

问题:Adaptation of argument list b

作者: Azur_wxj | 来源:发表于2018-03-18 15:51 被阅读40次

    在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: <none>
     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")
    }
    

    报的错误是:

    <console>:14: error: type mismatch;
     found   : Unit
     required: Unit => Unit
               println("Tom")
                      ^
    

    如果改成:

    sayHelloBeforeAction{
        ()=>println("Tom")
    }
    

    仍然会报错:

    <console>: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 b

          本文链接:https://www.haomeiwen.com/subject/izcztftx.html