1. 程式人生 > 其它 >實驗六 Scala的繼承和特質

實驗六 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.
  }
}