zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    第八章 1 元组简介
    第七章 5 字典生成式
    第七章 4 字典的视图操作、字典的遍历、字典的特点
    第七章 3 字典的常用操作(增删改查)
    第七章 2 字典的创建以及字典元素的获取
    第七章 1 字典介绍
    Java 中 Executors.newSingleThreadExecutor() 与Executors.newFixedThreadPool(1)有什么区别
    Java 使用线程池执行若干任务
    使用Jacoco获取 Java 程序的代码执行覆盖率
    基于 Golang 完整获取百度地图POI数据的方案
  • 原文地址:https://www.cnblogs.com/PerkinsZhu/p/9307460.html
Copyright © 2011-2022 走看看