实验六 Scala的继承和特质


目录
  • 一、实验目的
  • 二、实验内容
  • 三、实验要求
  • 四、实验步骤
    • 1.Scala中类的继承
    • 2.方法重写
    • 3.抽象类的继承和方法重写
    • 4.Scala中匿名类的使用
    • 5.检查和转换
    • 6.组合
    • 7.多态
    • 8创建Scala特质
    • 9混入特质以及特质和接口的区别
    • 10.Trait的构造顺序
    • 11.Trait扩展类
  • 五、思考题

一、实验目的

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,如图:

img

1.2创建包org.zkpk.scala

img

1.3在包org.zkpk.scala下创建Object Demo01.scala

imgimg

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点击运行,查看结果

image-20220406144056034

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运行程序,查看结果

image-20220406144542378

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运行程序,查看结果

image-20220406145216140

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点击运行,查看结果

image-20220406145735326

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”

img

6.3在object Demo06外创建实例类Cat,类Cat包含三个常量对象,分别是Head、Body、Feet,同时包含一个walk()方法,作用是调用Feet对象的walk方法

img

6.4在object Demo06中创建Cat对象,调用Cat对象的walk方法,实际调用Feet类的方法,简称组合

img

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()

img

7.3在object Demo07外创建类Student继承Person类,构造器包含两个参数name:String和age:Int,Student复写父类中的walk方法,输出字符串语句,并复写父类中的talkTo方法,输出字符串语句和参数对象Person的name

img

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

img

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方法返回的参数:

img

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典型错误详解,部分代码如下:

img

五、思考题

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.
  }
}