實驗六 Scala的繼承和特質
一、實驗目的
1、掌握Scala類的繼承。
2、掌握Scala中的多型。
3、掌握Scala中的特質的構造順序。
4、掌握Scala中的特質如何擴充套件類。
二、實驗內容
該實驗使用Scala的API學習類的繼承,熟悉繼承的相關概念及作用,熟悉多型的概念,掌握Scala基礎程式設計,瞭解特質與Java介面的異同,理解特質的構造順序,並利用特質實現擴充套件類。
三、實驗要求
1、JDK1.8環境
2、Scala-2.12.4(Scala-2.11.12)
3、本實驗基於Linux,可根據自身情況選擇平臺進行實驗。
4、根據實驗手冊的內容,按步驟完成練習。
四、實驗步驟
1.Scala中類的繼承
1.1開啟Scala IDE(IDEA community),建立Scala Project Test3,如圖:
1.2建立包org.zkpk.scala
1.3在包org.zkpk.scala下建立Object Demo01.scala
1.4在object Demo01外建立父類Animal類,構造器引數為空,且輸出字串“Animal”
class Animal{ println("Animal") }
1.5在object Demo01外建立子類Tiger類,繼承Animal類,Tiger類構造引數為空,且輸出字串“Tiger”
class Tiger extends Animal{
println("Tiger")
}
1.6在object Demo01中定義main方法,建立子類物件
object Demo01 {
def main(args: Array[String]): Unit = {
val tiger=new Tiger
}
}
1.7整體程式碼如下:
package base_test.test6 class Animal{ println("Animal") } class Tiger extends Animal{ println("Tiger") } object Demo01 { def main(args: Array[String]): Unit = { val tiger=new Tiger } }
1.8點選右鍵,依次選擇Run as--->Scala Application點選執行,檢視結果
2.方法重寫
2.1在org.zkpk.scala下建立Object Demo02.scala
2.2在object Demo02外建立父類Company,複寫toString方法,即在呼叫Company物件的toString方法時會返回字串“Company toString”:
class Company {
override def toString() = {
"Company toString"
}
}
2.3在object Demo02外建立子類Department繼承Company,複寫繼承的toString方法,即在呼叫Department物件的toString方法時會返回字串“Department toString”:
class Department extends Company{
override def toString(): String = {
"Department toString"
}
}
2.4在object Demo02中定義main方法,建立子類物件,列印輸出dp物件的toString方法
object Demo02 {
def main(args: Array[String]): Unit = {
val dp=new Department
println(dp.toString())
}
}
2.5整體程式碼如下:
package base_test.test6
class Company {
override def toString() = {
"Company toString"
}
}
class Department extends Company{
override def toString(): String = {
"Department toString"
}
}
object Demo02 {
def main(args: Array[String]): Unit = {
val dp=new Department
println(dp.toString())
}
}
2.6點選右鍵,依次選擇Run as--->Scala Application執行程式,檢視結果
3.抽象類的繼承和方法重寫
3.1在org.zkpk.scala下建立Object Demo03.scala
3.2在object Demo03外建立抽象父類Animal1,其中包含兩個抽象方法shout()和run():
abstract class Animal1 {
def shout()
def run()
}
3.3在object Demo03外建立非抽象類Tiger1繼承自Animal1,實現所有抽象方法:
class Tiger1 extends Animal1{
def shout()={
println("Tiger shout")
}
override def run(): Unit = {
println("Tiger run")
}
}
3.4建立抽象類Wolf繼承抽象父類Animal1,不需要實現具體方法
abstract class Wolf extends Animal1{
}
3.5在object Demo03中定義main方法,建立Tiger1例項物件,呼叫tiger物件的run()方法和shout()方法:
object Demo03 {
def main(args: Array[String]): Unit = {
val tiger1=new Tiger1
tiger1.run()
tiger1.shout()
}
}
3.6整體程式碼如下:
package base_test.test6
abstract class Animal1 {
def shout()
def run()
}
abstract class Wolf extends Animal1{
}
class Tiger1 extends Animal1{
def shout()={
println("Tiger shout")
}
override def run(): Unit = {
println("Tiger run")
}
}
object Demo03 {
def main(args: Array[String]): Unit = {
val tiger1=new Tiger1
tiger1.run()
tiger1.shout()
}
}
3.7點選右鍵,依次選擇Run as--->Scala Application執行程式,檢視結果
4.Scala中匿名類的使用
4.1在org.zkpk.scala下建立Object Demo04.scala)
4.2在object Demo04外建立抽象父類Animal2,其中包含兩個抽象方法shout()和run()
abstract class Animal2{
def shout()
def run()
}
4.3在object Demo04中建立抽象類Wolf2繼承自抽象類Animal2,不復寫父類中的任何方法
abstract class Wolf2 extends Animal2{
}
4.4在object Demo04中建立main方法,main方法中建立Wolf物件的匿名類,匿名類複寫了Wolf物件的shout方法和run方法:
object Demo04 {
def main(args: Array[String]): Unit = {
val wolf=new Wolf {// 建立匿名類,匿名類只能使用一次
def shout(){println("Wolf shout")}
def run(){println("Wolf run")}
}
wolf.run()
wolf.shout()
}
}
4.5整體程式碼如下:
package base_test.test6
abstract class Animal2{
def shout()
def run()
}
abstract class Wolf2 extends Animal2{
}
object Demo04 {
def main(args: Array[String]): Unit = {
val wolf=new Wolf {// 建立匿名類,匿名類只能使用一次
def shout(){println("Wolf shout")}
def run(){println("Wolf run")}
}
wolf.run()
wolf.shout()
}
}
4.6點選右鍵,依次選擇Run as--->Scala Application點選執行,檢視結果
5.檢查和轉換
5.1在org.zkpk.scala下建立Object Demo05.scala
5.2在object Demo05中建立main方法,程式碼如下:
package base_test.test6
object Demo05 {
def main(args: Array[String]): Unit = {
val department=new Department
// 判斷物件的型別是否給定的Department
if(department.isInstanceOf[Department]){
println("department物件的型別是Department")
}
if(department.isInstanceOf[Company]){
println("department物件的型別是Company")
// 型別轉換一下
val company=department.asInstanceOf[Company]
println(company.toString())
}
//getClass classOf[]
if(department.getClass == classOf[Department]){
println("department物件的型別是Department")
}
}
}
)
5.3點選右鍵,依次選擇Run as--->Scala Application執行程式,檢視結果
department物件的型別是Department
department物件的型別是Company
Department toString
department物件的型別是Department
6.組合
6.1在org.zkpk.scala下建立Object Demo06.scala
6.2在object Demo06外建立三個基本類:類Head包含一個think()方法,輸出字串“think”;類Body包含一個sleep()方法,輸出字串“sleep”;類Feet包含一個walk()方法,輸出字串“walk”
6.3在object Demo06外建立例項類Cat,類Cat包含三個常量物件,分別是Head、Body、Feet,同時包含一個walk()方法,作用是呼叫Feet物件的walk方法
6.4在object Demo06中建立Cat物件,呼叫Cat物件的walk方法,實際呼叫Feet類的方法,簡稱組合
6.5整體程式碼如下:
package base_test.test6
// 組合方式
class Head{
def think()=println("think")
}
class Body{
def sleep()={println("sleep")}
}
class Feet{
def walk()={println("walk")}
}
//組合的形式
class Cat{
val head:Head=new Head
val body:Body=new Body
val feet=new Feet
def walk(): Unit ={
feet.walk()
}
}
object Demo06 {
def main(args: Array[String]): Unit = {
val cat=new Cat
cat.walk()
}
}
6.6點選右鍵,依次選擇Run as--->Scala Application執行程式,結果如下
walk
7.多型
7.1在org.zkpk.scala下建立Object Demo07.scala
7.2在object Demo07外建立抽象類Person,Person的構造器引數為:String型別的name,Int型別的age,Person類包含兩個抽象方法,分別是:walk()和talkTo()
7.3在object Demo07外建立類Student繼承Person類,構造器包含兩個引數name:String和age:Int,Student複寫父類中的walk方法,輸出字串語句,並複寫父類中的talkTo方法,輸出字串語句和引數物件Person的name
7.4建立類Teacher繼承Person,構造器包含兩個引數name:String和age:Int,Teacher複寫父類中的walk方法,輸出字串語句,並複寫父類中的talkTo方法,輸出字串語句和引數物件Person的name,同時建立main方法,建立父型別指向子類物件:Person型別的Teacher和父類Person型別的Student物件,程式碼如下:
package base_test.test6
// 抽象person類
abstract class Person(var name:String,var age:Int){
def walk():Unit
//talkTo方法,引數是Persion型別
def talkTo(p:Person):Unit
}
class Student(name:String, age:Int) extends Person(name, age){
def walk()={println("walk like a alegant swan")}
def talkTo(p:Person)={
println("talkTo() method in Student")
println(this.name+" is talking to "+p.name)
}
}
class Teacher(name: String, age: Int) extends Person(name, age){
def walk()={println("walk like a alegant swan")}
def talkTo(p:Person)={
println("talkTo() method in Teacher")
println(this.name+" is talking to "+p.name)
}
}
object Demo07 {
def main(args: Array[String]): Unit = {
//下面兩行程式碼演示了多型的使用
//Person類的引用可以指向Person類的任何子類
val p1:Person=new Teacher("albert", 38)
val p2:Person=new Student("john", 28)
//下邊的兩行程式碼演示了動態繫結
//p1.talkTo(p2)傳入的實際型別是Student
//p2.talkTo(p2)傳入的實際型別是Teacher
//程式會根據實際型別呼叫對應的不同子類的talkto()方法
p1.talkTo(p2)
p2.talkTo(p1)
}
}
7.9依次選擇Run as--->Scala Application點選執行,檢視結果
talkTo() method in Teacher
albert is talking to john
talkTo() method in Student
john is talking to albert
8建立Scala特質
8.1在當前錄下建立檔案DAO.scala,程式碼如下:
package base_test.test6
trait DAO{
def delete(id:String):Boolean
def add(o:Any):Boolean
def update(o:Any):Int
def query(id:String):List[Any]
}
object TraitDemo01 {
}
8.2利用javap反編譯,檢視結果
scalac DAO.scala
javap –private DAO
9混入特質以及特質和介面的區別
9.1開啟Scala IDE,建立Scala工程Test5
9.2建立包org.zkpk.scala
9.3在包org.zkpk.scala下建立Object Demo01.scala
9.4在object Demo01外建立特質Mytrait,定義特質中的方法和欄位:delete方法,沒有方法體,為抽象方法; add方法,沒有方法體,為抽象方法;update方法,沒有方法體,為抽象方法; connectDB方法,包含具體方法體;抽象欄位sessionNum以及具體欄位port,程式碼如下:
trait MyTrait{
def delete(id:String):Boolean
def add(o:Any):Boolean
def update(o:Any):Int
//trait可以帶有具體實現的方法
def connectDB()={println("connect db")}
//可以帶有具體欄位
val port:Int=9999
//可以帶有抽象欄位
val sessionNum:Int
}
9.5建立例項類混入特質,實現特質中所有抽象方法,實現方法時,要根據方法的返回型別來定義:
class TraitImpl extends MyTrait{
def delete(id:String):Boolean=true
def add(o:Any):Boolean=true
def update(o:Any):Int=1
val sessionNum:Int=1
}
9.6在object Demo01中定義main方法,在main方法中,建立例項類物件,並呼叫物件的connectDB、add、delete、update方法和sessionNum屬性列印輸出,程式碼如下:
object TraitDemo {
def main(args: Array[String]): Unit = {
val tl=new TraitImpl
tl.connectDB()
println(tl.add())
println(tl.delete(""))
println(tl.update())
println(tl.sessionNum)
}
}
9.7整體程式碼如下:
package base_test.test6
trait MyTrait{
def delete(id:String):Boolean
def add(o:Any):Boolean
def update(o:Any):Int
//trait可以帶有具體實現的方法
def connectDB()={println("connect db")}
//可以帶有具體欄位
val port:Int=9999
//可以帶有抽象欄位
val sessionNum:Int
}
class TraitImpl extends MyTrait{
def delete(id:String):Boolean=true
def add(o:Any):Boolean=true
def update(o:Any):Int=1
val sessionNum:Int=1
}
object TraitDemo {
def main(args: Array[String]): Unit = {
val tl=new TraitImpl
tl.connectDB()
println(tl.add())
println(tl.delete(""))
println(tl.update())
println(tl.sessionNum)
}
}
9.8點選右鍵,依次選擇Run as---->Scala Application執行程式,檢視結果
connect db
true
true
1
1
10.Trait的構造順序
10.1在包org.zkpk.scala下建立Object Demo02.scala
10.2在object Demo02外建立父類和特質:
package base_test.test6
class FatherClass{
println("FatherClass constructor")
}
trait Trait1 extends FatherClass{
println("Trait1 constructor")
}
trait Trait2 extends Trait1{
println("Trait2 constructor")
}
trait Trait3 extends Trait1{
println("Trait3 constructor")
}
class TraitImpl1 extends Trait2 with Trait3{
println("TraitImpl1 constructor")
}
object TraitDemo01 {
}
建立類FatherClass,構造器引數為空,輸出字串語句“FatherClass constructor”,建立特質Trait1繼承類FatherClass,輸出字串語句“Trait1 constructor”,建立特質Trait2混入特質Trait1,輸出字串語句“Trait2 constructor”,建立特質Trait3混入特質Trait2,輸出字串語句“Trait3 constructor”,建立類TraitImpli混入特質Trait2和特質Trait3,構造器中輸出字元語句“TraitImpli constructor”:
10.3在object Demo02中建立main方法,在main方法中建立TraitImpli物件,觀察該物件的構造順序:
object TraitDemo01 {
def main(args: Array[String]): Unit = {
new TraitImpl1
}
}
10.4整體程式碼如下:
package base_test.test6
class FatherClass{
println("FatherClass constructor")
}
trait Trait1 extends FatherClass{
println("Trait1 constructor")
}
trait Trait2 extends Trait1{
println("Trait2 constructor")
}
trait Trait3 extends Trait1{
println("Trait3 constructor")
}
class TraitImpl1 extends Trait2 with Trait3{
println("TraitImpl1 constructor")
}
object TraitDemo001 {
def main(args: Array[String]): Unit = {
new TraitImpl1
}
}
10.5點選右鍵,選擇Run as---->Scala Application執行程式,檢視結果
FatherClass constructor
Trait1 constructor
Trait2 constructor
Trait3 constructor
TraitImpl1 constructor
11.Trait擴充套件類
11.1在包org.zkpk.scala下建立Object Demo03.scala
11.2在object Demo03外建立特質Logger,內容為空;
trait Logger{}
trait ExceptionLogger extends Exception with Logger{}
class ExceptionClass extends ExceptionLogger{
//那麼ExceptionLogger的父類Exception自動成為ExceptionClass的父類
def test()={
//getmessage是Exception類中的一個方法
println(getMessage)
}
}
建立特質ExceptionLogger繼承Exception類,並混入特質Logger;建立類ExceptionClass混入特質ExceptionLogger,那麼Exception自動成為特質ExceptionClass的父類:
11.5在object Demo03外建立類ExceptionClass1繼承IOException並混入ExceptionLogger特質,類中定義方法test方法,呼叫println方法輸出Exception類的getMessage方法返回的引數:
11.8 IOException是Exception子類,所以Exception是類ExceptionClass1的頂級父類,在object Demo03中定義main方法,main方法為空:
package base_test.test6
import java.io.IOException
trait Logger{}
trait ExceptionLogger extends Exception with Logger{}
class ExceptionClass extends ExceptionLogger{
//那麼ExceptionLogger的父類Exception自動成為ExceptionClass的父類
def test()={
//getmessage是Exception類中的一個方法
println(getMessage)
}
}
class ExceptionClass1 extends IOException with ExceptionLogger{
// IOExcepttion是Exception的子類,所以Excption是ExceptionClass1的頂級父類
def test()={
//getmessage是Exception類中的一個方法
println(getMessage)
}
}
object TraitDemo03 {
def main(args: Array[String]): Unit = {
}
}
11.8點選右鍵,依次選擇Run as---->Scala Application執行,執行無報錯,即為正確。
無報錯
11.9典型錯誤詳解,部分程式碼如下:
五、思考題
1.下面的指令碼定義了一個父類C和一個特質T,T繼承自C,然後定義了一個類C1混入特質T,最後例項化一個C1的變數,請檢查指令碼中的錯誤,並嘗試修改以得到期望的結果。
class C(val name:String)
trait T extends C{ def fly()}
class C1(val name:String) with T{
def fly(){ println(“I can fly.”)}
}
val t = new C1(“Scala”)
println(t.name) //期望輸出Scala
t.fly //期望輸出I can fly.
1、無論是繼承abstract class或者混入trait,對於一個要實現的類來說,必須先用extends,剩下的用with,否則編譯的時候就會出錯
正確的程式碼
class C(val name:String)
trait T extends C{
def fly()
}
class C1(name:String) extends C(name) with T{
override def fly(): Unit = {
println("I can fly.")
}
}
object A{
def main(args: Array[String]): Unit = {
val t = new C1("Scala")
println(t.name) //期望輸出Scala
t.fly //期望輸出I can fly.
}
}