Scala主构造器和辅助构造器详解

  • Post author:
  • Post category:其他




Java构造器


基本语法

[修饰符] 方法名(参数列表) {
    构造方法体
}


细节说明

1)在 Java 中一个类可以定义多个不同的构造方法(构造器),构造方法重载

2)如果程序员没有定义构造方法,系统会自动给类生成一个默认无参构造方法(也叫默认构造器)

3)一旦定义了自己的构造方法(构造器),默认的构造方法就覆盖了,就不能再使用默认的无参构造方法,除非显示的定义一下

4)子类的构造器默认会调用父类的空参构造器

5)如果子类显式的使用super()调用父类的有参构造器则不会再去调用父类的空参构造器


案例

public class JavaDemo {

    public static void main(String[] args) {
        //细节说明4
        new Student();    //调用了父类的空参构造器
        new Student("zhangsan", 170.0);    //调用了父类的空参构造器

        //细节说明5
        new Student("lisi", 18, 175.0);    //调用了父类的带参构造器
    }
}

class Person {
    String name;
    int age;

    public Person() {
        System.out.println("调用了父类的空参构造器");
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("调用了父类的带参构造器");
    }
}

class Student extends Person {
    double height;

    public Student() {
    }

    public Student(String name, int age, double height) {
        super(name, age);
        this.height = height;
    }

    public Student(String name, double height) {
        this.name = name;
        this.height = height;
    }
}



Scala构造器


介绍

1)和 Java 一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法(即 scala 中构造器也支持重载)

2)Scala 类的构造器包括:主构造器和辅助构造器


基本语法

class 类名(形参列表) {    //主构造器
    // 类体
    def this(形参列表) {    //辅助构造器
    }
    
    def this(形参列表) {    //辅助构造器可以有多个,编译器通过不同参数来区分
    }
}


细节说明

1)Scala 构造器作用是完成对新对象的初始化,构造器没有返回值

2)主构造器的声明直接放置于类名之后

3)主构造器会执行类定义中的所有语句,这里可以体会到 Scala 的函数式编程和面向对象编程融合在一起,即:构造器也是方法(函数),传递参数和使用方法和前面的函数部分内容没有区别

4)如果主构造器无参数,小括号可省略,构建对象时调用的构造方法的小括号也可以省略

5)辅助构造器名称为 this(这个和 Java是不一样的),多个辅助构造器通过不同参数列表进行区分,在底层就是构造器重载。辅助构造器,必须在第一行显式调用主构造器(可以是直接,也可以是间接)

6)如果想让主构造器变成私有的,可以在 () 之前加上 private,这样用户只能通过辅助构造器来构造对象了,示例:class Person private() {}

7)辅助构造器的声明不能和主构造器的声明一致,会发生错误(即构造器名重复)

8)子类的主构造器默认会调用父类的空参构造器,这就会直接或间接的调用父类的主构造器


案例

.scala代码:

object ScalaConstructor {

  def main(args: Array[String]): Unit = {
    //1. 使用主构造器
    val person1 = new Person1("Tom", 18)
    println(person1.toString)
    //执行了主构造器的其他语句:age=28
    //输出:name=Tom, age=28

    println("**************************")

    //2. 使用辅助构造器
    val person2 = new Person1("Tom")
    println(person2.toString)
    //执行了主构造器的其他语句:age=20
    //输出:name=Tom, age=20
  }
}

//主构造器:初始化对象的age属性和name属性
class Person1(inName: String, inAge: Int) {
  var name: String = inName
  var age: Int = inAge

  age += 10
  println("执行了主构造器的其他语句:" + "age=" + age)

  //重写toString,便于输出对象的信息
  override def toString: String = {
    "name=" + this.name + ", age=" + this.age
  }

  //辅助构造器:初始化对象的name属性
  def this(name: String) {
    //辅助构造器,必须在第一行显式调用主构造器(可以是直接,也可以是间接)
    this("jack", 10)
    //如果主构造器是空参构造器,则可以写成this或this()
    this.name = name //对name重新赋值
  }

  def this() {
    this("jack", 10)
  }
}

Person1的反编译代码:

//可以看出scala主构造器和辅助构造器在底层都是java中的构造器,并没有主次之分
public class Person1 {
  private String name;
  
  private int age;
  
  public String name() {
    return this.name;
  }
  
  public void name_$eq(String x$1) {
    this.name = x$1;
  }
  
  public int age() {
    return this.age;
  }
  
  public void age_$eq(int x$1) {
    this.age = x$1;
  }
  
  public String toString() {
    return (new StringBuilder()).append("name=").append(name()).append(", age=").append(BoxesRunTime.boxToInteger(age())).toString();
  }
  
  public Person1(String name) {
    this("jack", 10);
    name_$eq(name);
  }
  
  public Person1() {
    this("jack", 10);
  }
  
  public Person1(String inName, int inAge) {
    this.name = inName;
    this.age = inAge;
    age_$eq(age() + 10);
    Predef$.MODULE$.println((new StringBuilder()).append(").append(BoxesRunTime.boxToInteger(age())).toString());
  }
}



Scala构造器参数的修饰符


说明

1)Scala 类的主构造器的形参未用任何修饰符修饰,那么这个参数是局部变量

2)如果参数使用 val 关键字声明,那么 Scala 会将参数作为类的私有的只读属性使用

3)如果参数使用 var 关键字声明,那么 Scala 会将参数作为类的成员属性使用,并会提供属性对应的 xxx()[类似 getter] / xxx_$eq()[类似 setter]方法,即这时的成员属性是私有的,但是可读写


案例

.scala代码:

object ScalaConstructor1 {

  def main(args: Array[String]): Unit = {
    val worker1 = new Worker1("zhangsan")
    //println(worker1.inName)  //不能访问inName, 会报错

    val worker2 = new Worker2("lisi")
    println(worker2.inName) //可以访问inName, lisi

    val worker3 = new Worker3("wangwu")
    println(worker3.inName) //可以访问inName, wangwu
    worker3.inName = "zhaoliu" //可以修改inName,
    println(worker3.inName) //zhaoliu
    println(worker3.name) //wangwu
  }
}

//1. 如果主构造器是Worker(inName: String),那么inName就是一个局部变量
class Worker1(inName: String) {
  var name = inName
}

//2. 如果主构造器是Worker2(val inName: String) ,那么inName就是Worker2的一个private的只读属性
class Worker2(val inName: String) {
  var name = inName
}

// 如果主构造器是Worker3(var inName: String),那么inName就是Worker3的一个private的可读写的属性
class Worker3(var inName: String) {
  var name = inName
}

Worker1的反编译代码:

//inName变成Worker1构造器的局部变量,不能访问
public class Worker1 {
  private String name;
  
  public String name() {
    return this.name;
  }
  
  public void name_$eq(String x$1) {
    this.name = x$1;
  }
  
  public Worker1(String inName) {
    this.name = inName;
  }
}
Worker2的反编译代码:

//inName变成Worker2的私有不可变的属性,提供了 public String inName() {} 方法来访问
public class Worker2 {
  private final String inName;
  
  private String name;
  
  public String inName() {
    return this.inName;
  }
  
  public Worker2(String inName) {
    this.name = inName;
  }
  
  public String name() {
    return this.name;
  }
  
  public void name_$eq(String x$1) {
    this.name = x$1;
  }
}

Worker3的反编译代码:

//inName变成Worker2的私有属性,提供了 public String inName() {} 方法来访问和 public void inName_$eq(String x$1) {} 来修改
public class Worker3 {
  private String inName;
  
  public String inName() {
    return this.inName;
  }
  
  public void inName_$eq(String x$1) {
    this.inName = x$1;
  }
  
  private String name = inName();
  
  public String name() {
    return this.name;
  }
  
  public void name_$eq(String x$1) {
    this.name = x$1;
  }
  
  public Worker3(String inName) {}
}



版权声明:本文为wjt199866原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。