scala类参数修饰符浅析

来源:互联网 发布:excel画出数据形状 编辑:程序博客网 时间:2024/05/22 16:48

case类的类参数修饰符

case类的类参数修饰符有两种:val和var,默认是val,比如:
case class User(name:String, pwd:String)
就相当于
case class User(val name:String, val pwd:String)
从翻译的字节码也可看出

{  private final java.lang.String name;    descriptor: Ljava/lang/String;    flags: ACC_PRIVATE, ACC_FINAL  private final java.lang.String pwd;    descriptor: Ljava/lang/String;    flags: ACC_PRIVATE, ACC_FINAL  public java.lang.String name();    descriptor: ()Ljava/lang/String;    flags: ACC_PUBLIC    Code:      stack=1, locals=1, args_size=1         0: aload_0         1: getfield      #39                 // Field name:Ljava/lang/String;         4: areturn  public java.lang.String pwd();    descriptor: ()Ljava/lang/String;    flags: ACC_PUBLIC    Code:      stack=1, locals=1, args_size=1         0: aload_0         1: getfield      #43                 // Field pwd:Ljava/lang/String;         4: areturn

所以,默认情况下,case类是不能有成员变量的set方法的。
同时我们也注意到scala编译器自动为User生成了name()和pwd()两个public的get方法。在类之外这样调用:

def testUser = {    val u =  User("wala", "123")    println(u.name + "=>" + u.pwd)}

字节码层面调的其实是name()和pwd()方法,而并非真的在访问类的私有成员:

        19: invokespecial #31                 // Method scala/collection/mutable/StringBuilder."<init>":()V        22: aload_1        23: invokevirtual #35                 // Method com/huawei/test/User.name:()Ljava/lang/String;        ......        34: aload_1        35: invokevirtual #44                 // Method com/huawei/test/User.pwd:()Ljava/lang/String;        38: invokevirtual #39                 // Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder;        41: invokevirtual #47                 // Method scala/collection/mutable/StringBuilder.toString:()Ljava/lang/String;        44: invokevirtual #51                 // Method scala/Predef$.println:(Ljava/lang/Object;)V        47: return

当然,我们也可以改变默认行为,将类参数设成var:

case class User(var name:String, var pwd:String)

这时,scala编译器就会为我们生成set方法,见以下字节码:

{  private java.lang.String name;    descriptor: Ljava/lang/String;    flags: ACC_PRIVATE  private java.lang.String pwd;    descriptor: Ljava/lang/String;    flags: ACC_PRIVATE  public java.lang.String name();    descriptor: ()Ljava/lang/String;    flags: ACC_PUBLIC    Code:      stack=1, locals=1, args_size=1         0: aload_0         1: getfield      #39                 // Field name:Ljava/lang/String;         4: areturn  public void name_$eq(java.lang.String);    descriptor: (Ljava/lang/String;)V    flags: ACC_PUBLIC    Code:      stack=2, locals=2, args_size=2         0: aload_0         1: aload_1         2: putfield      #39                 // Field name:Ljava/lang/String;         5: return  public java.lang.String pwd();    descriptor: ()Ljava/lang/String;    flags: ACC_PUBLIC    Code:      stack=1, locals=1, args_size=1         0: aload_0         1: getfield      #46                 // Field pwd:Ljava/lang/String;         4: areturn  public void pwd_$eq(java.lang.String);    descriptor: (Ljava/lang/String;)V    flags: ACC_PUBLIC    Code:      stack=2, locals=2, args_size=2         0: aload_0         1: aload_1         2: putfield      #46                 // Field pwd:Ljava/lang/String;         5: return

可以看到,在name()和pwd()之外,编译器还为我们生成了name_$eq和pwd_$eq两个set方法。
这样调用:

def testUser = {    val u = User("wala", "123")    u.name = "chuchu"    println(u.name + "=>" + u.pwd)  }

相当于调用了name_$eq,而非直接访问成员变量。

非case类的类参数修饰符

非case类的类参数在明确指明了var和val的情形下,行为跟case类是类似的。但若不指明是var还是val,行为比较复杂,有时甚至不会生成成员变量,比如:

class User( name:String,  pwd:String)

会为我们生成这样的字节码:

{  public com.huawei.test.User(java.lang.String, java.lang.String);    descriptor: (Ljava/lang/String;Ljava/lang/String;)V    flags: ACC_PUBLIC    Code:      stack=1, locals=3, args_size=3         0: aload_0         1: invokespecial #13                 // Method java/lang/Object."<init>":()V         4: return}

可以看到,没有成员变量,也没有get、set方法,这样的类定义有啥用处?
但也不尽然,看下面的例子:

class User( name:String,  pwd:String) {  def f = name + "$"  def g = pwd + "$"}

生成的字节码如下:

{  private final java.lang.String name;    descriptor: Ljava/lang/String;    flags: ACC_PRIVATE, ACC_FINAL  private final java.lang.String pwd;    descriptor: Ljava/lang/String;    flags: ACC_PRIVATE, ACC_FINAL  public java.lang.String f();    descriptor: ()Ljava/lang/String;    flags: ACC_PUBLIC    Code:      stack=2, locals=1, args_size=1         0: new           #15                 // class scala/collection/mutable/StringBuilder         3: dup         4: invokespecial #19                 // Method scala/collection/mutable/StringBuilder."<init>":()V         7: aload_0         8: getfield      #21                 // Field name:Ljava/lang/String;        11: invokevirtual #25                 // Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder;        14: ldc           #27                 // String $        16: invokevirtual #25                 // Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder;        19: invokevirtual #30                 // Method scala/collection/mutable/StringBuilder.toString:()Ljava/lang/String;        22: areturn  public java.lang.String g();    descriptor: ()Ljava/lang/String;    flags: ACC_PUBLIC    Code:      stack=2, locals=1, args_size=1         0: new           #15                 // class scala/collection/mutable/StringBuilder         3: dup         4: invokespecial #19                 // Method scala/collection/mutable/StringBuilder."<init>":()V         7: aload_0         8: getfield      #35                 // Field pwd:Ljava/lang/String;        11: invokevirtual #25                 // Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder;        14: ldc           #27                 // String $        16: invokevirtual #25                 // Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder;        19: invokevirtual #30                 // Method scala/collection/mutable/StringBuilder.toString:()Ljava/lang/String;        22: areturn  public com.huawei.test.User(java.lang.String, java.lang.String);    descriptor: (Ljava/lang/String;Ljava/lang/String;)V    flags: ACC_PUBLIC    Code:      stack=2, locals=3, args_size=3         0: aload_0         1: aload_1         2: putfield      #21                 // Field name:Ljava/lang/String;         5: aload_0         6: aload_2         7: putfield      #35                 // Field pwd:Ljava/lang/String;        10: aload_0        11: invokespecial #37                 // Method java/lang/Object."<init>":()V        14: return}

scala编译器为我们生成了private final的成员变量,但并未生成任何get、set方法,只是在f、g这样的成员函数里才能访问name和pwd。所以,非case类的类参数未指定var和val时,所起的作用相当于仅生成private成员变量,而无任何的get、set方法。其实,这样的做法在某些场景下更符合数据封装的概念

原创粉丝点击