经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Scala » 查看文章
case class 和class的区别以及构造器参数辨析
来源:cnblogs  作者:PerKins.Zhu  时间:2018/9/25 20:28:16  对本文有异议

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

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

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

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

 

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

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

源码:

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

 反编译结果:

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

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

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

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

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

源码:

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

 反编译结果:

  1. Compiled from "Test.scala"
  2. public class DD {
  3. private final java.lang.String name;
  4. private java.lang.String name();
  5. public DD(java.lang.String);
  6. }
  7. Compiled from "Test.scala"
  8. public class EE {
  9. public EE(java.lang.String);
  10. }

 结论: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. 1 case class FF(name: String)

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

反编译结果:

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

  分析:

  先看FF.class

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

  1. 1 public class AA
  2. 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. 1 case class GG(val name: String)
  2. 2 case class HH(var name: String)
  3. 3 case class II(private val name: String)
  4. 4 case class JJ(private[this] val name: String)

 

 

 

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

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

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

-----end

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号