Scala 面向对象(中)
1、继承和多态
用法和Java基本一致。
基本语法
class 子类名 extends 父类名称 {}
案例演示
object Test07_Inherit {def main(args: Array[String]): Unit = {val student = new Student1("GG Bond",18)student.printInfo()}}//定义一个父类
class Person1(){var name: String = _var age: Int = _println("1. 父类主构造器调用")def this(name: String,age: Int){this()println("2.父类的辅助构造器被调用")this.name = namethis.age = age}def printInfo(): Unit = {println(s"${name} ${age}")}
}
//继承父类可以指定调用父类的构造器(需要给继承的父类传入参数,使用父类的主构造器还是辅助构造器取决于传入的参数是什么) 也可以不调用(直接extends Person1)
class Student1(name: String,age: Int) extends Person1(name,age){var school: String = _println("3. 子类的主构造器被调用")def this(name: String,age: Int,school: String){this(name,age) //调用主构造方法this.school = schoolprintln("4. 子类的辅助构造器被调用")}//重写父类的方法override def printInfo(): Unit = {println(s"${name} ${age} ${school}")}
}
2、多态
2.1、动态绑定
Java中,只有方法的动态绑定的,但在Scala中,方法和属性都是动态绑定的。
Java:
public class Test {public static void main(String[] args) {Worker worker = new Worker();System.out.println(worker.name);worker.hello();System.out.println("============");//多态Person person = new Worker();System.out.println(person.name);person.hello();}}class Person{String name = "person";public void hello(){System.out.println("hello person");}
}class Worker extends Person{String name = "worker";public void hello(){System.out.println("hello worker");}
}
输出结果
worker
hello worker
============
person
hello worker
Scala:
Scala在重写父类的属性和方法时,需要再属性名和方法名之前使用 “override” 关键字。
package chapter05object Test08_DynamicBind {def main(args: Array[String]): Unit = {val student:Student8 = new Student8println(student.name)student.hello()println("=============")//多态val person:Person8 = new Student8println(person.name)person.hello()}}class Person8{val name = "person"def hello(): Unit = {println("hello person")}
}class Student8 extends Person8 {override val name = "student"override def hello(): Unit = {println("hello student")}
}
运行结果
student
hello student
=============
student
hello student
可以看到,Scala通过父类创建子类对象后,在调用对象的方法时,会动态绑定当前的对象的属性和方法。
3、抽象类
3.1、抽象属性和方法
Scala中定义变量和方法时一般必须要求赋初始值,但是在抽象类中可以不遵守。
- 在Scala中,只要一个类中有抽象方法和抽象属性,该类必须是抽象类。
- 在Scala中,一个抽象类的属性和方法可以是已实现的。
- 定义抽象类:abstract class Person{} //通过 abstract 关键字标记抽象类
- 定义抽象属性:val|var name:String //一个属性没有初始化,就是抽象属性
- 定义抽象方法:def hello():String //只声明而没有实现的方法,就是抽象方法
3.2、抽象类的继承与重写
- 如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明为抽象类
- 重写非抽象方法需要用 override 修饰,重写抽象方法则可以不加 override。
- 子类中调用父类的方法使用 super 关键字
- 子类对抽象属性进行实现,父类抽象属性可以用 var/val 修饰;
- 子类可以直接对一个被 var 修饰的变量进行赋值来实现重写,不需要使用 override 关键字,所以对于非抽象属性,不建议用 var 修饰。
abstract class Person9{//抽象属性val name: String//非抽象属性val age: Int = 18 //被val 修饰 子类需要重写var school = "山西农业大学" //被var修饰 子类不需要重写//抽象方法def hello():Unit//非抽象方法def eat(): Unit = {println("person eat")}}class Student9 extends Person9{//实现抽象属性val name: String = "李元芳"//实现抽象方法def hello(): Unit = {println("hello")}//重写非抽象属性override val age = 10school = "北京中医药大学" //父类中被 var 修饰的非抽象变量可以直接重新赋值 不需要重写 所以不建议非抽象属性用 var 修饰//重写非抽象方法override def eat(): Unit = {super.eat()println("student eat")}}
4、匿名子类
匿名子类,也就是说不需要继承抽象父类,直接在定义对象的时候重写抽象属性和方法。
object Test {def main(args: Array[String]): Unit = {val person:Person10 = new Person10 {override var name: String = "GG Bond"override def hello(): Unit = {println("hello gays")}println(person.name)person.hello()}}}//定义抽象类
abstract class Person10{//定义抽象属性和方法var name: Stringdef hello(): Unit}
5、单例对象(伴生对象)
Scala语言是 完全面向对象 的语言,所以并没有静态的操作(即在 Scala 中没有静态的概念)。但是为了能够和Java 语言交互(因为 Java 中有静态概念),就产生了一种特殊的对象来 模拟类对象, 该对象为 单例对象 。若单例对象名与类名一致,则称该单例对象这个类的 伴 生对象 ,这个类的所有“静态”内容都可以 放置在它的伴生对象 中声明。
说明
- 单例对象采用 object 关键字声明
- 单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
- 单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。
- 伴生类和伴生对象必须名字相同且在一个文件中。
object Test {def main(args: Array[String]): Unit = {val bob = new Student11("bob", 18)bob.printInfo()}}//定义伴生类
class Student11(val name: String,val age: Int){def printInfo(): Unit = {println(s"${name} ${age} ${Student11.school}")}}
//定义伴生对象
object Student11{val school: String = "北京中医药大学"}
5.1、主构造器私有化
有些时候我们不希望直接通过主构造器来创建对象实例,而是通过工厂方法来创建对象,在Scala中,我们可以借助伴生对象和伴生类可互相调用的特点来实现:
我们需要在伴生类的主构造器参数前使用 private 关键字进行修饰:
object Test {def main(args: Array[String]): Unit = {
// val bob = new Student11("bob", 18)
// bob.printInfo()val bob = Student11.newStudent("bob", 18)bob.printInfo()}}//定义伴生类
class Student11 private (val name: String,val age: Int){def printInfo(): Unit = {println(s"${name} ${age} ${Student11.school}")}}
//定义伴生对象
object Student11{val school: String = "北京中医药大学"//定义一个类的对象实例的创建方法(工厂方法)def newStudent(name: String,age: Int): Student11 = {new Student11(name,age)}}
5.2、apply方法
使用 apply方法来实现工厂模式,可直接使用类名来创建对象,不需要显式地使用apply方法。
object Test {def main(args: Array[String]): Unit = {val alice = Student11.apply("alice",10)val tom = Student11("alice",10) //scala 调用apply方法可以省去方法名 简化代码alice.printInfo()tom.printInfo()}}//定义伴生类
class Student11 private (val name: String,val age: Int){def printInfo(): Unit = {println(s"${name} ${age} ${Student11.school}")}}
//定义伴生对象
object Student11{val school: String = "北京中医药大学"def apply(name: String,age: Int): Student11 = {new Student11(name,age)}}
5.3、单例设计模式
单例设计模式,始终只有一个对象,即使我们在创建对象的时候,赋予不同的属性,但是返回的结果都只会是第一个对象的实例。
object Test12_Singleton {def main(args: Array[String]): Unit = {val student2 = Student12.getInstance("bob",20)student2.printInfo() //bob 20 北京中医药大学val student1 = Student12.getInstance("alice",18)student1.printInfo() //bob 20 北京中医药大学//输出相同的对象引用println(student1) println(student2)}}
//定义伴生类
class Student12 private (val name: String,val age: Int){def printInfo(): Unit = {println(s"${name} ${age} ${Student12.school}")}}//饿汉式
//object Student12{
// val school = "北京中医药大学"
//
// private val student: Student12 = new Student12("alice",18)
//
// def getInstance(): Student12 = student
//}//懒汉式
object Student12{val school = "北京中医药大学"private var student: Student12 = _def getInstance(name: String,age: Int): Student12 = {if (student == null){//没有对象实例的话就创建一个student = new Student12(name,age)}student}
}
使用伴生对象可以很轻松地实现各种设计模式。