zoukankan      html  css  js  c++  java
  • 除去Scala的糖衣(13) -- Default Parameter Value

    欢迎关注我的新博客地址:http://cuipengfei.me/

     

    好久没有写博客了,上一次更新居然是一月份。

    说工作忙都是借口,咋有空看美剧呢。

    这半年荒废掉博客说究竟就是懒,惯性的懒惰。写博客这事儿,一丢掉就非常久捡不起来。

    闲话到此为止。以下进入正题。

    Default parameter value,默认參数值。 这个非常easy理解,给參数一个默认值,假设调用者不显式指明參数值。则使用默认值。

    假设显式指明了,那就用显式指明的值。

    举个样例:

    1
    
    def hello(name: String = "world") = println("hello " + name)
    

    这个函数。假设我们不给它传參数,它就会打印hello world。

    就像这样:

    1
    
    hello()
    

    假设给了參数:

    1
    
    hello("everybody")
    

    则打印hello everybody。

    这个语言特性都有哪些应用场景呢?

    它经经常使用来避免过多的重载。

    一个非经常见非常典型的样例就是构造函数重载。

    在Java中,为了让调用者可以比較easy的创建某个类的实例。我们一般会提供几个參数列表比較短的构造函数。而这些构造函数存在的唯一意义就是为了写死某几个參数值。而在Scala中。有了这个语言特性,我们就无需那么麻烦了。

    那这个语言特性是怎样实现的呢?实际上简单的一塌糊涂。

    这样一段代码:

    1
    2
    3
    4
    5
    6
    7
    
    class Greeter {
      def hello(name: String = "world") = println("hello " + name)
    }
    
    class AnotherClass {
      new Greeter().hello()
    }
    

    我们的Greeter类含有前面提到过的hello方法。在AnotherClass里调用了hello,而且没有显式指明參数值。

    上面的Scala代码生成的bytecode反编译成Java是这种:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    public class Greeter {
        public void hello(String name) {
            Predef..MODULE$.println(new StringBuilder().append("hello ").append(name).toString());
        }
    
        public String hello$default$1() {
            return "world";
        }
    }
    
    public class AnotherClass {
        public AnotherClass() {
            Greeter qual$1 = new Greeter();
            String x$1 = qual$1.hello$default$1();
            qual$1.hello(x$1);
        }
    }
    

    能够看到,我们所定义的hello方法反编译出来看起来非常普通,就是个接受一个參数的方法。

    而在Greeter类中,编译器为我们增加了还有一个方法hello$default$1,这种方法就是返回一个写死的字符串,其值为world。

    在AnotherClass中调用hello时,写死的字符串被取到。然后传进了hello里。

    这样。被调用者提供了參数的默认值,调用者在调用时取得该值,然后传入方法。

    题外话

    到这里我不禁联想起C#中的默认參数值的实现方式。

    在C#中。默认參数的值会被编译成调用者的一个常量,而不是像Scala一样的由被调用者提供。

    这样看起来貌似没啥差别,不就是写死的值换个地方吗?

    事实上不然,假设被调用者在A程序集内,调用者在B程序集内,那么A更新时,B就必须又一次编译才干得到最新的默认值。也就是说,假设当前部署环境中同一时候存在A和B。而后我们拿一个新版的A来替换老的,这时B仍然在传递老的默认參数值给A。这样就会造成一些看似非常诡异的行为偏差。

    假设对C#的默认參数值的实现有兴趣。请看我非常久非常久之前写的博客

    如今想来,C#这一语言特性的设计者为什么要把它设计成如此easy出错的样子呢?

    思而不得其解。

  • 相关阅读:
    死锁
    钩子函数和回调函数的区别
    蓝绿部署、滚动发布、灰度发布的介绍以及最佳实践
    小公司的瓶颈
    Modbus协议详解
    windows+jenkin
    Java:简单的多态实例
    一、Kubernetes系列之介绍篇
    Shell脚本自动搭建ipsec环境
    Appium(1):安卓自动化环境搭建 + Android SDK + Appium 环境搭建
  • 原文地址:https://www.cnblogs.com/lxjshuju/p/6860901.html
Copyright © 2011-2022 走看看