Scala中的特质(trait)篇

一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素,所以在使用时,也采用了extends关键字,如果有多个特质存在父类,那么需要采用with关键字连接
➢没有父类
class 类名 extends 特质1 with 特质2 with 特质3…
➢有父类
class 类名 extends 父类 with 特质1 with 特质2 with 特质3

trait的声明:

trait 特质名{
    trait体
}



Scala中没有接口(interface)的概念, 类似于接口
简单例子来看一下trait的样貌:

Object Scala_Demo{
    def main(args: Array[String]) : Unit = {
          var user = new User()
          println(user)
     }
}
声明特质
trait TestTrait{
  
}
特质可以继承, 所以使用extends关键字
class User extends TestTrait{

}
输出: 
zhang.shao.User@6eceb130


如果类继承多个特质,采用with连接
演示:

trait TestTrait{

}
trait TestTrait2{

}
class User extends TestTrait with TestTrait2{

}


如果一个类同时存在父类和特质, 依然采用继承方式,但是继承的是父类, 连接(混入)特质
class 类名 extends 父类 with 特质1 with 特质2 with 特质3
演示:

trait TestTrait{

}
trait TestTrait2{

}
class Person{

}
class User extends Person with TestTrait with TestTrait2{
    
}



java中的接口无法直接构建对象,必须使用实现类
scala中的特质也无法构建对象
在这里插入图片描述



scala中的特质是可以执行代码的. 比如把println打印语句放入特质中。
java接口中不可以放执行代码的.
在这里插入图片描述
演示:

package zhang.shao

object 演示区 {
    def main(args: Array[String]) : Unit = {
        val user = new User
    }
}
trait TestTrait{
   println("hello scala")
}

class User extends TestTrait{

}
输出:
hello scala

注:

  1. 一般执行的代码是放到方法里或者静态代码块或者代码块. 而Scala可以支持在特质里直接放入执行代码。 很方便!! 而java中在接口里是不能直接放入执行的代码…
  2. 查看反编译内容,可以看到特质里面有个初始化方法! 当创建类对象时,会首先执行特质内的内容.然后再执行构造方法里的内容!!
    在这里插入图片描述
  3. 可以看到先执行了特质里的内容,然后再执行构造方法里的内容



java中的接口中可以声明方法的, 早期版本中声明的方法都是抽象,新版本的是可以有默认实现
java中的接口中可以声明属性的, 属性值无法修饰
scala特质中声明的属性和方法都可以在混入类(继承类,如下面代码中的User100类)中调用, 同时特质中声明的属性还可以修改.
演示:

package zhang.shao

object 演示区 {
  def main(args: Array[String]): Unit = {
  
    val user10 = new User100()
    println(user10.name)  // 打印了特质里的属性值
    user10.name = "王五"   // 修饰了特质里的属性
    println(user10.name)
    user10.test   // 调用了特质里的方法
  }
}
trait TestTrait100 {
    // 声明属性
    var name : String = "李四"
    // 声明方法
    def test = {
      println("hello world")
    }
}
class User100 extends TestTrait100 {

}



特质中可以声明抽象方法或者声明抽象属性.
演示:

package zhang.shao

object 演示区 {
  def main(args: Array[String]): Unit = {
    val user10 = new User100()
    user10.test1
    println(user10.username)
    println(user10.age)

  }
}
trait TestTrait100 {
    声明抽象方法
    def test1()
    声明抽象属性
    var username : String
    声明一个变量
    val age : Int = 20
}

class User100 extends TestTrait100 {
  重写抽象方法
  def test1: Unit = {
     println("编程爱好者")
  }
  重写抽象属性
  override var username: String = "iloveyou"
  重写父类中的变量, 切记不能重写var声明的非抽象变量, 只能用val来重写非抽象属性
  override val age : Int = 100
}



子类,父类, 以及特质的执行顺序
特质和父类没有关系, 只和当前的混入的类(如例子中的User100类)有关系,所以, 在调用时, 父类先执行, 然后当前混入的特质再执行, 然后当前类再执行

package zhang.shao

object 演示区 {
  def main(args: Array[String]): Unit = {
    val user10 = new User100()
  }
}
// 特质(特征)
trait TestTrait100 {
   println("tarit code....")
}
// 父类
class Person100{
  println("parent code ......")
}
// 子类
class User100 extends Person100 with TestTrait100 {
   println("child code ...")
}
输出:
parent code ......
tarit code....
child code ...:
1. 特质TestTrait100只和User100类有关系,和Person100类没有关系
2. Person100只和User100有关系. 与TestTrait100没有关系!!
3. 一个类有父类和特质,  父类先执行, 特质再执行,然后子类再执行

再看另一个例子:

如果父类和子类都混入了相同的特质,那么特质的代码只会执行一遍!

package zhang.shao

object 演示区 {
  def main(args: Array[String]): Unit = {
    val user10 = new User100()
  }
}
trait TestTrait100 {
   println("tarit code....")
}
class Person100 extends TestTrait100{
  println("parent code ......")
}
class User100 extends Person100 with TestTrait100 {
   println("child code ...")
}
输出:
tarit code....
parent code ......
child code ...:
1. 特质的内容只执行了一遍, 在Person100之前执行了一次, 到了User100的时候就没有执行特质内容.
2. 一个类有父类和特质,  父类先执行, 特质再执行,然后子类再执行



特质可以继承其他的特质, 特质还可以重写父特质的方法
演示:

object Scala_Demo {
  def main(args: Array[String]): Unit = {
    
    val user = new User()
    user.insert()
  }
}
父特质, 父特质中有个方法(不是抽象方法)
trait Operate{
   def insert(){
       println("插入数据")
   }
}
子特质, 子特质可以重写父特质的方法. 用override关键字, 如果是抽象方法可以不用override, 如果是非抽象方法必须要用override
trait DB extends Operate{
    override def insert(){
         print(“向数据库”)
         super.insert()  
    }
}
class MySql extends DB{
   
}

输出:
向数据库插入数据

假如一个类混入了多个特质, 会怎么样??
演示:

object Scala_Demo {
  def main(args: Array[String]): Unit = {
    
    val user = new User()
    user.insert()
  }
}
父特质, 父特质中有个方法(不是抽象方法)
trait Operate{
   println("Operate....")
   def insert(){
       println("插入数据")
   }
}
子特质, 子特质可以重写父特质的方法. 用override关键字, 如果是抽象方法可以不用override, 如果是非抽象方法必须要用override
trait DB extends Operate{
    println("DB....")
    override def insert(){
         print(“向数据库”)
         super.insert()  
    }
}

trait File extends Operate{
     println("File......")
    override def insert(){
        print("向文件")
        super.insert()  
    }
}
class MySql extends File with  DB{
   
}

输出:
Operate......
File......
DB......
向数据库插入数据

注:
1. 多特质混入时, 可执行代码的执行顺序为从左到右, 如果有父特质, 会优先执行, 如果混入了两个特质(DB和File)有公共的父特质(Operate),那么父特质的代码只会执行一遍
2. 多特质混入时, 方法调用的顺序为从右到左!
3. 特质中的super关键字不是指代父特质, 指代的是上一级特质,从例子来看, DB特质上一级特质为File特质而不是Operate特质, File特质上一级特质没有了,super就会自动的指代父特质!
4. 如果希望super指向父特质, 需要增加特殊操作: super[特质]

如果希望super指向父特质, 需要增加特殊操作: super[特质]
在这里插入图片描述
此时就不会执行File特质里的insert方法内容!!




java的接口可以在scala中当成特质来使用
在这里插入图片描述




Trait动态混入
根据开放封闭原则,当需要在原来的代码中增加新的功能时,不允许修改原来的代码,即不能更改之前的类。为此,Scala引入了动态混入的语法,可以在创建对象时动态地添加新的Trait, 比如用extends关键字来, 就会造成了子类的功能的扩展, 既然是功能的扩展了, 就相当于在类的基础上混入了这个特质, 就改变了之前的代码, 也就对之前的代码进行了修改. 违背了OCP开发原则!!
演示:

package zhang.shao

object 演示区 {
  def main(args: Array[String]): Unit = {
     动态混入, 扩展性非常强!!
     val user20 = new User200 with Operate5
     user20.show
     
  }
}
trait Operate5{
   def show = {
      println("展示数据")
   }
}
class User200 {

}

在这里插入图片描述




特质继承类
在这里插入图片描述




自身类型(selftype)
有很多特质不是所有类都能随随便便继承的,例如异常类所能继承的特质,其他类继承并不合适,所以需要对特质加以约束。
在这里插入图片描述
关于自身类型可以看下面文章:
https://yanbin.blog/scala-self-type-understanding/#more-8528

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值