• scala 学习笔记(03) 参数缺省值、不定个数参数、类的属性(Property)、泛型初步


    继续学习,这一篇主要是通过scala来吐槽java的,同样是jvm上的语言,差距咋就这么大呢?

    作为一个有.NET开发经验的程序员,当初刚接触java时,相信很多人对java语言有以下不爽(只列了极小一部分):

    1. 一堆的setter/getter方法,没有c#中的property属性概念

    2. 方法的参数值,不能设置缺省值

    3. 不定个数参数的写法太单一

    ...

    然后java的拥护者讲出一堆大道理,说这样设计是如何如何有道理,各种洗脑,时间长了,也就被迫习惯了。要不是遇到scala,我还真就信了,你看看人家scala同学,2003/2004发布的,早就把这些全实现了,而java同学作为jvm上的元老,这些年一直顽固不化,不思进取,已经被jvm上的其它同学远远甩在后面了,java你可长点心吧!进入正题,直接上码:

    一、参数缺省值

      /**
       * 参数缺省值
       * @param person
       * @param msg
       */
      def saySomething(person: String = "somebody", msg: String = "Hello") = {
        println(person + " say : " + msg);
      }
    

     调用示例:

        saySomething()
        saySomething("jimmy")
        saySomething("jimmy", "hi")
    

    当然这里有一个小小的限制,如果要用参数缺省值,建议所有的参数全设置缺省值,如果只给部分参数设置缺省值,函数定义不会有问题,调用时,上面的示例编译就通不过了(大意是提供的参数不足之类),大家可以把msg参数的缺省值去掉再试试。

    那么,最终编译出来的class,到底是如何实现的呢?可以借助一些反编译工具,比如JD-GUI还原成java一看究竟:

        public void saySomething(String person, String msg) {
            Predef..MODULE$.println(new StringBuilder().append(person).append(" say : ").append(msg).toString());
        }
    
        public String saySomething$default$1() {
            return "somebody";
        }
    
        public String saySomething$default$2() {
            return "Hello";
        }
    

    也就是说,scala中的def saySomething(person: String = "somebody", msg: String = "Hello") 如果用java实现的话,可以用3个方法来变相实现,每个缺省参数,相当于一个独立的版本,换言之,在编译器层面,其实java的编译器如果想做,是完全可以做到的,为什么不做?懒!顽!

    二、class的property

    /**
     * 定义一个带参主构造器的类
     * @param pReadOnly
     */
    class Sample(pReadOnly: String) {
    
      /**
       * 可读写的属性
       */
      var myProperty: String = _;
    
      private val _readOnly: String = pReadOnly;
    
      /**
       * 只读属性
       */
      def readOnly: String = _readOnly;
    
    }
    

    调用示例:

        val sample = new Sample("test")
        println(sample.readOnly)
        sample.myProperty = "a new value"
        println(sample.myProperty)
    

    没了setter/getter看起来倍儿清爽!还是反编译class看看:

    public class Sample
    {
      private String myProperty;
      private final String _readOnly;
    
      public String myProperty()
      {
        return this.myProperty; } 
      public void myProperty_$eq(String x$1) { this.myProperty = x$1; } 
      private String _readOnly() {
        return this._readOnly;
      }
    
      public String readOnly()
      {
        return _readOnly();
      }
    
      public Sample(String pReadOnly)
      {
        this._readOnly = pReadOnly;
      }
    }
    

    可以看到,myProperty自动生成了setter/gettter,仍然是在编译器层面,就可以顺手做掉的事情,java编译器依然不肯做。

    三、不定个数参数值

    这个问题,java中虽然可以xxx(String[] args)用数组传递达到类似的效果,但是就算传一个空数组,也至少也得写一个xxx(null)吧,既然此时参数都为空了,为啥不直接xxx()更直接,看看scala:

      /**
       * 不固定个数的参数
       * @param x
       * @return
       */
      def add(x: Int*) = {
        var i = 0
        for (j <- x) i += j
        i
      }
    

     调用:

        println(add())
        println(add(1, 2, 3, 4, 5))
    

     明显的更高端大气上档次,继续反编译,这个要略复杂点:
    先是生成了这么一个类:

    public final class DefHello$$anonfun$add$1 extends AbstractFunction1.mcVI.sp
      implements Serializable
    {
      public static final long serialVersionUID = 0L;
      private final IntRef i$1;
    
      public final void apply(int j)
      {
        apply$mcVI$sp(j); } 
      public void apply$mcVI$sp(int j) { this.i$1.elem += j; }
    
    
      public DefHello$$anonfun$add$1(IntRef i$1)
      {
      }
    }
    

    然后是:

       public void main(String[] args)
        {
            Predef..MODULE$.println(BoxesRunTime.boxToInteger(add(Nil..MODULE$)));
            Predef..MODULE$.println(BoxesRunTime.boxToInteger(add(Predef..MODULE$.wrapIntArray(new int[] { 1, 2, 3, 4, 5 }))));
            ...
        }
    
        public int add(Seq<Object> x)
        {
            IntRef i = IntRef.create(0);
            x.foreach(new AbstractFunction1.mcVI.sp() { public static final long serialVersionUID = 0L;
    
                public final void apply(int j) { apply$mcVI$sp(j); }
                public void apply$mcVI$sp(int j) { DefHello..this.elem += j; }
    
            });
            return i.elem;
        }
    

    最终调用时,add()这里虽然scala没有传任何参数,但从反编译结果上看,最终还是变成了add(Nil..MODULE$)),编译器自动加了一个参数,以满足java的规范。

    四、泛型初步

    java中的泛型是一个"伪"泛型,其类型擦除机制只是障眼法而已,因此带来了很多使用上的限制,比如下面这个例子:

    public class SampleClass<T> {
        private T _t;
    
        public SampleClass(T t) {
            this._t = t;
        }
    
        public T getT() {
            return _t;
        }
    }
    

     这里定义了一个泛型类,如果想创建一个该类的数组:

    SampleClass<String>[] objs = new SampleClass<String>[10];
    

    编译器会直接报错:Error: java: generic array creation,原因是:type erase后,内部已经是SampleClass[],按OOP的原则,可以向上转型为Object[],这下可好了,Object是万能类型,如果向这个万能类型的数组里加入一个不是SampleClass<String>的实例,理论上也是允许的,这就违背了泛型约束的初衷。

    但是在scala中,却是可以这样做的,看下面的代码:

    class MyClass[T](t1: T) {
      var t: T = t1;
    }
    

    然后可以这样用:

        val objs = new Array[MyClass[String]](10)
        objs(0) = new MyClass[String]("a")
        for (x <- objs; if x != null) println(x.t)
    

    编译和运行一切正常,这是什么情况?还是反编译解密:

        MyClass[] objs = new MyClass[10];
    
        objs[0] = new MyClass("a");
    
        Predef..MODULE$.refArrayOps((Object[])objs).withFilter(new DefHello..anonfun.main.1()).foreach(new DefHello..anonfun.main.2());
    

    原来,对于java的伪泛型机制,scala早就看穿了这一切,因此它采用了一种略带"极端"的做法,直接使用原始类型,无情的对java的泛型机制回应:『不约,我们不约』。

    了解以上这些后,我不得不更加佩服坚持使用java语言写出这么多NB开源框架的达人们,硬是用一个要啥啥没有的语言为开源世界做出这么大的贡献,这是一种什么样的精神,无禁让我想起了《道士下山》中猿击术中的精髓:"不离不弃,不嗔不恨!",我只想说:这么多年,你们是怎么忍下来的!

    So,Scala既然这么好,就完美无缺了么?当然不是,功能越强大,语法越灵活,自然学习成本也更高。另外,性能方面,它生成的字节码感觉比java略多,网上有很多关于scala与java的性能讨论,包括google也有类似的评测,有人说这二者差不多,但是多数人还是认为在jvm上,scala的性能整体来看要低于java,只能达到java的8成上下(详情可自行百度,有很多这类文章)

  • 相关阅读:
    MySQL修改配置,区分大小写
    mvc中validateinput属性在asp.net4中不工作
    VS2010开发环境最佳字体及配色
    推荐19个很有用的 JavaScript 库
    Mysql limit 优化,百万至千万级快速分页,复合索引的引用并应用于轻量级框架
    C:\Windows\system32\MSVCR100.dll 没有被指定在 Windows 上运行,或者它包含错误。请尝试使用原始安装媒体重新安装程序,或联系您的系统管理员或软件供应商以获取支持。【解决办法】
    log4net配置步骤
    TraceSource记录程序日志
    [转] WPF TextBox控件中文字实现垂直居中
    SQL Server实现类似split功能
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/4714558.html
Copyright © 2020-2023  润新知