Scala类详解

本文深入探讨Scala中类的定义、构造函数的使用及如何生成getter和setter方法。通过实例展示不同构造器的调用过程及Scala如何处理类成员变量。
一个简单的Scala类:
class Person {
    private var name: String = _
    private var age = 0

    def this(name: String, age: Int) { //实际上this是重载了主构造器,使用new Person()依然可以创建对象
        this //默认构造器,即主构造器,class Person实际上省略了(),class Person()是主构造器
        this.name = name
        this.age = age
    }

    def getName() = name //或则def getName = name
    def setName(name: String) { this.name = name }
}

object ClassTest {
    def main(args: Array[String]): Unit = {
        val a = new Person()
        println(a.getName())

        val b = new Person("zero", 18)
        println(b.getName())
    }
}
运行结果:
null
zero
如注释,this方法是重载了主构造器,使用new Person()依然可以创建对象,在this方法中必须先调用this默认构造器,即主构造器,class Person实际上省略了(),class Person()是主构造器。和Java不一样,这里new Person()依然可以创建对象,如果想禁掉这种创建类的方式可以这样:
class Person private () {
    private var name: String = _
    private var age = 0

    def this(name: String, age: Int) {
        this
        this.name = name
        this.age = age
    }

    def getName() = this.name
    def setName(name: String) { this.name = name }
}
需要注意的是,在这种方式下定义类成员时必须要显式的指明初始值

当然还可以更简单的定义这个Person类:
class Person(val name: String, var age: Int, gender: String, number: Int) {
    def getNumber() = this.number;
}

object ClassTest {
    def main(args: Array[String]): Unit = {
        val b = new Person("zero", 18, "男", 7)
        b.age_=(20)
        println(b.name)
        println(b.age)
    }
}
使用jd-gui-windows-1.4.0(一下简称jd-gui)反编译Person.class后的结果如下:
public class Person{
  private final String name;
 
  public String name(){
    return this.name;
  }
 
  public int age(){
    return this.age;
  }
 
  public void age_$eq(int x$1){
    this.age = x$1;
  }
 
  public int getNumber(){
    return this.number;
  }
 
  public Person(String name, int age, String gender, int number) {}
}
再使用javap –private Person.class反编译结果如下:
public class com.zero.clazz.Person {
  private final java.lang.String name;
  private int age;
  private final int number;
  public java.lang.String name();
  public int age();
  public void age_$eq(int);
  public int getNumber();
  public com.zero.clazz.Person(java.lang.String, int, java.lang.String, int);
}
结合两个工具的反编译结果分析:Scala自动将这个类变成了public,在Scala中任何没有标记为private或则protected的数据都默认为public。Scala将声明为var的age成员用private修饰,并对外提供getter和setter方法用以取值和赋值(在Scala中,getter和setter分别叫做age和age_=,默认的getter和setter是由Scala自动生成),将声明为val的成员用private final修饰,并对外提供getter方法用以取值。对于既不是var也不是val的成员,如果在类中没有被使用,那么就忽略了该字段,如果被使用了(只能够取值不能赋值),那个该字段就晋升为类的成员,被private final修饰。

如果不想由Scala生成getter和setter方法呢?
class Person(val name: String) {
    private var age = 0
    private[this] var gender: String = "男"
}
javap反编译结果:
public class com.zero.clazz.Person {
  private final java.lang.String name;
  private int age;
  private java.lang.String gender;
  public java.lang.String name();
  private int age();
  private void age_$eq(int);
  public com.zero.clazz.Person(java.lang.String);
}
可以发现,private [this] var gender类似于定义对象的私有字段,其他同一个类的对象也无法访问到,Scala不会生成getter或setter方法。

关于构造函数
class Person(val name: String, var age: Int, gender: String) {
    private var firstName: String = _
    def this(name: String, age: Int, gender: String, firstName: String) {
        this(name, age, gender)
        this.firstName = firstName
        println("construct_02")
    }
    println("construct_01")

    override def toString(): String = {
        age + " zero " + firstName
    }
}

object ClassTest {
    def main(args: Array[String]): Unit = {
        val a = new Person("zero", 18, "男")
        println(a)

        val b = new Person("zero", 18, "男", "007")
        println(b)
    }
}
运行结果:
construct_01
18 zero null
construct_01
construct_02
18 zero 007
jd-gui反编译后的Person:
public class Person{
  private final String name;
  private String firstName;
 
  public String name(){
    return this.name;
  }
 
  public int age(){
    return this.age;
  }
 
  public void age_$eq(int x$1){
    this.age = x$1;
  }
 
  private String firstName(){
    return this.firstName;
  }
 
  private void firstName_$eq(String x$1){
    this.firstName = x$1;
  }
 
  public Person(String name, int age, String gender, String firstName){
    this(name, age, gender);
    firstName_$eq(firstName);
    Predef..MODULE$.println("construct_02");
  }
 
  public Person(String name, int age, String gender){
    Predef..MODULE$.println("construct_01");
  }
 
  public String toString(){
    return new StringBuilder().append(age()).append(" zero ").append(firstName()).toString();
  }
}
javap反编译后的结果:
public class com.zero.clazz.Person {
  private final java.lang.String name;
  private int age;
  private java.lang.String firstName;
  public java.lang.String name();
  public int age();
  public void age_$eq(int);
  private java.lang.String firstName();
  private void firstName_$eq(java.lang.String);
  public java.lang.String toString();
  public com.zero.clazz.Person(java.lang.String, int, java.lang.String);
  public com.zero.clazz.Person(java.lang.String, int, java.lang.String, java.lan
g.String);
}
发现实际上scala生成了两个构造函数,主构造函数Person(String name, int age, String gender),辅助构造器也调用了主构造函数,主构造函数会将类{}里面的语句纳入到其方法类。

如果喜欢传统的JavaBean风格的getter与setter,可以这样:
class Person(@BeanProperty val name: String,
        @BeanProperty var age: Int,
        @BeanProperty gender: String) {
}
javap反编译结果:
public class com.zero.clazz.Person {
  private final java.lang.String name;
  private int age;
  public java.lang.String name();
  public int age();
  public void age_$eq(int);
  public void setAge(int);
  public java.lang.String getName();
  public int getAge();
  public com.zero.clazz.Person(java.lang.String, int, java.lang.String);
}
可以发现将会生成四个方法,除了Scala默认的setter和getter方法,还有JavaBean风格的getter与setter方法。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值