工作中偶然发现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 produ