scala中的传名调用


scala函数调用的参数传递有两种方式:

  • 一种是传值调用,这种方式和C语言的函数参数传递方式类似,在函数调用的时刻,计算各个参数的值,然后传递进入函数内部,scala中写作function(x: Int);
  • 一种是传名调用,这种调用方式在函数调用时并不直接计算参数的具体值,而是在函数中使用到该参数时,才进行计算,scala中写作function(x: =>Int)。这里有意思的地方在于,在scala中, Int => Int表示一个函数类型,接受Int参数,返回Int返回值。那这里x的类型,就类似于一个函数,它没有参数,但是返回一个Int类型的返回值。 下面看一个并没有什么卵用,只为用于说明特性的例子:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package example

    object TestThunk extends Greeting with App {
    def printAndReturnInt() = {
    println("test...")
    1
    }
    callByValue(printAndReturnInt)
    callByName(printAndReturnInt)
    }

    trait Greeting {
    def callByValue(x: Int) = {
    println("first call by value: x=" + x)
    println("second call by value: x=" + x)
    }
    def callByName(x: => Int) = {
    println("first call by name: x=" + x)
    println("second call by name: x=" + x)
    }
    }

调用结果输出是:

1
2
3
4
5
6
7
test...
first call by value: x=1
second call by value: x=1
test...
first call by name: x=1
test...
second call by name: x=1

通过test…的输出,可以看出传名调用和传值调用的区别。


那这个传名调用,有什么作用呢?

首先,在部分情况下它可以提高效率:比如函数中没有用到这个传名调用参数的时候,这个参数就不会被计算。
但是,既然不会用到这个参数,你传它进函数干嘛?其实也是有这种情况的,比如熟悉的getOrElse,它的默认取值default就是一个传名调用:

def getOrElse[B >: A](default: ⇒ B): B  

另外,这种延迟参数计算到函数中实际调用位置的方式,也为scala的一些特性提供了支撑,比如Try:

Try的apply方法,接收的就是一个传名参数:

1
2
3
4
def apply[T](r: => T): Try[T] =
try Success(r) catch {
case NonFatal(e) => Failure(e)
}

只有在构造Try时使用传名参数r,将r的调用推迟到Try内部,才能够在Try内部对异常进行处理;否则的话,r将在传入Try内部之前进行计算,在计算中出现的异常将会直接抛到Try外部,就不能实现Try对于异常的包装了,是不是很神奇很有用?


推荐阅读:

scala模式匹配的一个问题
打通Python和C++
待业青年

转载请注明出处: http://blog.guoyb.com/2018/01/21/scala-call-by-name/

欢迎使用微信扫描下方二维码,关注我的微信公众号TechTalking,技术·生活·思考:
后端技术小黑屋

Comments