类比下 Java构造器
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 类的构造器包括:主构造器和辅助构造器
3)Scala中辅助构造器 必须使用this, 调用 主构造器 或者上个 辅助构造器,即 第一个 辅助构造器 用this 调用主构造器, 第二个辅助构造器 使用this 调用第一个辅助构造器。
eg:
所以,第二个 辅助构造器 会先调用第一个辅助构造器,第一个辅助构造器又调用 主构造器, 所以会先执行 主构造器 然后 第一个辅助构造器 依次往后执行
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) {}
}