• case class 和class的区别以及构造器参数辨析


    工作中偶然发现Scala构造方法中的参数,无论是否有val/var修饰都可以顺利编译运行,如下:

    1     class AA(name: String)
    2     class BB(val name: String)

    那么两者的区别在哪里呢?对于case class呢?其区别又在哪里?其应用场景又在哪里呢?下面就辨析一下如下几个类的区别

     1     class AA(name: String)
     2     class BB(val name: String)
     3     class CC(var name: String)
    4 class DD(private val name: String) 5 class EE(private[this] val name: String)
    6 case class FF(name: String) 7 case class GG(val name: String) 8 case class HH(var name: String) 9 case class II(private val name: String) 10 case class JJ(private[this] val name: String)

     单纯的从代码中来看,发现不了什么区别,只是简单的多了一个val的修饰符。为了一探究竟,先对源码进行编译,然后通过javap对其class文件进行反编译,查看其与源码的区别。

    一、普通类构造器中val/var 存在和不存在的区别

    源码:

    1     class AA(name: String)
    2     class BB(val name: String)
    3     class CC(var name: String)

     反编译结果:

     1 Compiled from "Test.scala"
     2 public class AA {
     3   public AA(java.lang.String);
     4 }
     5 
     6 Compiled from "Test.scala"
     7 public class BB {
     8   private final java.lang.String name;
     9   public java.lang.String name();
    10   public BB(java.lang.String);
    11 }
    12 
    13 Compiled from "Test.scala"
    14 public class CC {
    15   private java.lang.String name;
    16   public java.lang.String name();
    17   public void name_$eq(java.lang.String);
    18   public CC(java.lang.String);
    19 }

      结论:构造器中val修饰的参数,编译之后会在该类中添加一个private final全局常量,同时提供一个用于获取该常量的public方法,无设置方法。

       构造器中var修饰的参数,编译之后会在该类中添加一个private全局变量,同时提供两个获取和设置该变量值的public方法。

        构造器中无修饰符的参数,则该参数属于构造函数内的局部参数,仅仅在该构造函数内部访问

    二、普通类构造器中private和private[this] 修饰参数的区别

    源码:

    1     class DD(private val name: String)
    2     class EE(private[this] val name: String)

     反编译结果:

    Compiled from "Test.scala"
    public class DD {
      private final java.lang.String name;
      private java.lang.String name();
      public DD(java.lang.String);
    }
    
    Compiled from "Test.scala"
    public class EE {
      public EE(java.lang.String);
    }

     结论:private 修饰的构造器参数,编译之后会在该类中添加一个private final的全局常量,同时提供一个private的访问该参数的方法。即该参数可在该类范围内访问

       private[this]修饰的构造器参数,不存在全局变量,只能在该构造方法中访问,在该类中无法访问。同 class AA(name: String)

    注意:Scala整个类体就是其构造函数,所以,站在Scala角度看,private[this]修饰的构造器参数能够在整个类中访问,而站在Java角度看,该参数仅仅能够在构造函数中访问,在类中无法访问。而站在Scala角度看,private[this]和 private的主要区别在于,private[this]修饰的参数无法通过e.name的方式访问,即使在该类的内部。注意下图:

    图中,在EE#show中是无法访问that.name的,即使that的类型本身就是EE也不行的。这才是private[this]和private在Scala中的主要区别。

     三、普通class和case class的区别

    源码:

    1   case class FF(name: String)

     编译之后会发现在文件夹下面多出两个class文件,一个为FF.class,另一个为FF$.class文件。对两个文件进行反编译

    反编译结果:

     1 Compiled from "Test.scala"
     2 public class FF implements scala.Product,scala.Serializable {
     3     //private final 修饰的name常量
     4     private final java.lang.String name;
     5     //public修饰获取name的方法,可用于外部访问
     6     public java.lang.String name();
     7     //public修饰的构造函数
     8     public FF(java.lang.String);
     9     public static scala.Option<java.lang.String> unapply(FF);
    10     public static FF apply(java.lang.String);
    11     public static <A> scala.Function1<java.lang.String, A> andThen(scala.Function1<FF, A>);
    12     public static <A> scala.Function1<A, FF> compose(scala.Function1<A, java.lang.String>);
    13     public FF copy(java.lang.String);
    14     public java.lang.String copy$default$1();
    15     public java.lang.String productPrefix();
    16     public int productArity();
    17     public java.lang.Object productElement(int);
    18     public scala.collection.Iterator<java.lang.Object> productIterator();
    19     public boolean canEqual(java.lang.Object);
    20     public int hashCode();
    21     public java.lang.String toString();
    22     public boolean equals(java.lang.Object);
    23 }
    24 
    25 Compiled from "Test.scala"
    26 public final class FF$ extends scala.runtime.AbstractFunction1<java.lang.String, FF> implements scala.Serializable {
    27     //静态的FF$对象
    28     public static FF$ MODULE$;
    29     //构造函数为private
    30     private FF$();
    31     
    32     //返回FF对象的 apply方法
    33     public FF apply(java.lang.String);
    34     
    35     public static {};
    36     public final java.lang.String toString();
    37     public scala.Option<java.lang.String> unapply(FF);
    38     private java.lang.Object readResolve();
    39     public java.lang.Object apply(java.lang.Object);
    40 }

      分析:

      先看FF.class

       1、对比class AA(name: String)的结果来看,case class 自动实现了scala.Product,scala.Serializable两个特质(接口),因此,case class类中自然也会实现这两个特质中的抽象方法/覆写一些方法(11~22行)。

    1 public class AA
    2 public class FF implements scala.Product,scala.Serializable 

        2、对比 public class BB,在类内部都具有一个全局常量name和一个获取该常量的public方法,同时两者都具有一个public的构造函数。

       3、实现了一些特质中的一些方法,覆写了Java Object中的一些方法。例如:andThen() toString() hashCode()等

       再看FF$

        1、有一个public  static修饰名为 MODULE$ 的 FF$对象

        2、构造器被私有化,用private修饰。

        3、组合1、 2、两点可知,FF$是一个单例类。其矢志不渝就是Scala中FF类的伴生对象。 

     结论

      Scala编译器在对case class进行编译的时候做了特殊处理,扩展了其方法和功能,加入了scala.Product,scala.Serializable的特性,同时为其提供了该类的伴生对象。另外,对于case class 构造器参数,其默认以public修饰,可允许外部调用

     四、其他

     上面几个对比理解了,下面这几个类之间的区别也就可以举一反三了。

    1     case class GG(val name: String)
    2     case class HH(var name: String)
    3     case class II(private val name: String)
    4     case class JJ(private[this] val name: String)

    =========================================

    原文链接:case class 和class的区别以及构造器参数辨析 转载请注明出处!

    =========================================

    -----end

  • 相关阅读:
    oracle中Blob和Clob类型的区别
    为什么要分库分表
    Enable file editing in Visual Studio's debug mode
    SQL Server Dead Lock Log
    Debug .NET Framework Source
    SQL Server text field里面有换行符的时候copy到excel数据会散乱
    诊断和修复Web测试记录器(Web Test Recorder)问题
    Can't load Microsoft.ReportViewer.ProcessingObjectModel.dll
    'telnet' is not recognized as an internal or external command
    Linq to XML
  • 原文地址:https://www.cnblogs.com/PerkinsZhu/p/9307460.html
Copyright © 2020-2023  润新知