scala类参数修饰符浅析

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方法。其实,这样的做法在某些场景下更符合数据封装的概念

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值