【scala系列】10、class(类)、object(对象)、trait(特质)


1. 对象(object)

在scala 中被object 关键字修饰的类有一下特征

  1. 没有 有参数的主构造器, 但是有主构造器代码块(不包含在任何方法中的代码,就是主构造器代码)
  2. 它是单例的所以主构造器代码块只会执行一次
  3. 不需要通过关键字 new 创建对象,直接通过类名创建对象
  4. 通常用于封装一些常量,工具类,枚举和隐式转换函数

1.1 对象的定义


object ObjectDemo1 {
  def main(args: Array[String]): Unit = {
    println("ObjectDemo1 的主构造器代码块1")
    def sayHello = println("hello ! ")
  }
}


object ObjectDemo1_1 {
  def main(args: Array[String]): Unit = {
    val o1 = ObjectDemo1
    val o2 = ObjectDemo1

    println(o1.hashCode() == o2.hashCode()) //true
  }
}

object ObjectDemo1_2 extends App{
  println("ObjectDemo1_2 的主构造器代码块1")
}

1.2 对象作为枚举类


object ObjectDemo2 extends App{

  println(MyEnum.WOLF) //Wolf

  println(MyEnum.withName("Wolf")) //Wolf

  //遍历枚举值
  val values:MyEnum.ValueSet = MyEnum.values
  values.foreach(println) //Wolf ,dog ,cat

  //通过下标获取枚举值
  println(MyEnum(0)) //Wolf
}

object MyEnum extends Enumeration{

  val WOLF = Value(0,"Wolf")
  println(s"WOLF=$WOLF")

  val DOG = Value(1,"dog")
  println(s"DOG=$DOG")

  val CAT = Value(2,"cat")
  println(s"CAT=$CAT")
}

2. 类(class)

类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。类是用于创建对象的蓝图,它是一个定义包括在特定类型的对象中的方法和变量的软件模板。

2.1 类的定义

class Class1 {

  // 在scala 类中定义字段有两个关键字 val 和 var
  // 使用val修饰的字段属于常量,只能访问变量的值,不能修改变量的值
  val name:String = "class1"

  // 使用var修饰的字段属于变量,既能访问变量的值,也能修改变量的值
  var age:Int =15

  val country = "China"

  // 使用private [this] 修饰的成员属性/方法只能在本类中访问
  private [this] val  color="red"

  // color 在外部不能直接访问,可以在本类中使用方法访问后返回
  def  getColor= {
    // color 是使用private [this] 修饰的只能在本类中访问
    color
  }

  // def 定义成员方法, 每次调用时重新计算
  def email = {
    name + "@mail.com"
  }
}

/**
  * 限制类的访问范围只能在classpag 包下
  */
private [classpag] class Class2 {
  val name:String = "class2"
}

// 类的实例化
val c1 = new Class1

println(c1.country) // China

println(c1.getColor) //red

// val 定义的属性只读不可写
// c1.country = "Japan" // 报错

println(c1.email) // class1@mail.com

// 无法访问到getColor 方法和 color属性

2.2 类的构造器(构造方法)

在scala 中类的构造器分为两种

  1. 主构造器:与类名交织在一起,一个类只能定义一个主构造器

    • 创建对象的顺序一定是主构造器最先执行
    • 主构造器的参数会成为类的字段属性
    • 在主构造器参数中,没有被任何关键字修饰的参数,默认为private [this] var/val
    • 不包含在任何方法中的代码,就是主构造器的代码
  2. 辅助构造器:辅助构造器可以定义多个

    • 辅助构造器的第一行代码必须调用主构造器
    • 创建对象的顺序一定是主构造器最先执行

class Class3(
        val name:String, // 定义常量成员属性
        var age:Int, // 定义成员变量
        height:Int, // 此时 height 相当于使用private this val 修饰的,只能在本类中使用
        private var brothday2:String, // 定义成员变量,只能在奔类中访问
        var color:String="blue" // 定义默认值的成员变量,只能在参数的末尾
   ){

  println("这是主构造器的代码")

  // 定义成员变量
  var sex:String=""

  println(s"print in Class3 height= ${this.height}")

  // 定义一个辅助构造器1
  // 辅助构造器中的属性不能使用val 或者var来修饰
  def this(name:String,age:Int){
    //辅助构造器的第一行代码必须是调用主构造器
    this(name,age,170,null,null)
    this.sex = "男"
    println("辅助构造器1")
  }

  //可以定义多个辅助构造器2
  def this(name:String){
    this(name,16) //辅助构造器可以调用辅助构造器
    this.brothday2="birthday2"
    println("辅助构造器2")
  }

}

object ClassDemo2 {
  def main(args: Array[String]): Unit = {

    // 调用主构造器实例化类
    val c3 = new Class3("xiaom",18,180,"birthday")


    println("name=>"+c3.name)
    println("age=>"+c3.age)
    println("color=>"+c3.color)
    println("sex=>"+c3.sex)
    /**
      * 这是主构造器的代码
      * print in Class3 height= 180  //height 只能在Class3 类中访问到
      * name=>xiaom
      * age=>18
      * color=>blue // 使用默认值 blue
      * sex=>
      */

    // 调用 辅助构造器1 实例化类
    val c3_1 = new Class3("xiaob",19)
    println("name=>"+c3_1.name)
    println("age=>"+c3_1.age)
    println("color=>"+c3_1.color)
    println("sex=>"+c3_1.sex)

    /**
      * 这是主构造器的代码
      * print in Class3 height= 170  //height 只能在Class3 类中访问到
      * 辅助构造器1
      * name=>xiaob
      * age=>19
      * color=>null //因为在使用构造器的时候传的是null 所以这里不用默认值
      * sex=>男
      */

    // 调用 辅助构造器2 实例化类
    val c3_2 = new Class3("xiaos")
    println("name=>"+c3_2.name)
    println("age=>"+c3_2.age)
    println("color=>"+c3_2.color)
    println("sex=>"+c3_2.sex)

    /**
      * 这是主构造器的代码
      * print in Class3 height= 170
      * 辅助构造器1
      * 辅助构造器2
      * name=>xiaos
      * age=>16
      * color=>null
      * sex=>男
      */
  }
}

3. 伴生类与伴生对象

  • 定义:

    • 在同一个scala的类文件中
    • 如果class和object的名字相同
    • 那么class是object的伴生类
    • object是class的伴生对象
  • 特点:

    • 伴生类与伴生对象可以互相访问被关键字private修饰的字段,需要注意的是被private this 修饰的对象或属性不能访问
    • 可以通过伴生类与伴生对象创建对象
  • 创建伴生类与伴生对象

// class ClassDemo4 是 object ClassDemo4 的伴生类
class ClassDemo4 {

  //被private修饰的字段只能在本类及其伴生对象中访问
  private val name:String = "xiaom"

  //被 private [this] 修饰的对象,只能在本类中访问,伴生对象也不能访问
  private [this] var tel:String = "15000000000"

  //向外部暴露get和set方法 以对private 修饰的字段进行 取值与赋值
  def getTel() = {
    this.tel
  }

  def setTel(tel:String): Unit ={
    this.tel = tel
  }
}

// object ClassDemo4 是 class ClassDemo4 的伴生对象
object ClassDemo4 {
  def main(args: Array[String]): Unit = {
    val c4 = new ClassDemo4
    println(c4.name) //xiaom

    //tel 被 private [this] 修饰,伴生对象中也不能访问
    //println(c4.tel)

    // 需要通过类中定义的方法访问 tel 属性
    c4.setTel("new number")
    println(c4.getTel()) //new number
  }

}

object ClassDemo4_1 {
  def main(args: Array[String]): Unit = {
    val c4 = new ClassDemo4
    // 不是 ClassDemo4 的伴生对象则不能访问name 属性
    //println(c4.name) //error
  }
}
  • 通过伴生类与伴生对象创建对象
class MyArray{//伴生类
  private var name:String = "小明"
  println(MyArray.name)
}

object MyArray{ // 伴生对象
  private var name:String = "小红"
  println(new MyArray().name)

  def apply(nums:Int*):Array[Int] = {
    println("apply(nums:Int*)")
    val arr = new Array[Int](nums.length)
    for(i<- 0 to nums.length -1){
      arr(i) = nums(i)
    }
    arr
  }
}
object ObjectDemo05 {
  def main(args: Array[String]): Unit = {
    var myarr1 = MyArray(5) // 通过伴生对象创建对象 通过调用apply方法创建出来的对象不相同
    var myarr2 = new MyArray() //通过伴生类创建对象
  }
}

3.1 apply 方法

当对象(伴生对象)以函数的方式进行调用时,scala 会隐式地将调用改为在该对象上调用apply方法。
Scala 的apply有2张形式,一种是伴生对象的apply,一种是伴生类中的apply。


// object ClassDemo5 是 class ClassDemo5 的伴生对象
object ClassDemo5 {

  println("hello world")//主构造代码块

  def apply():String={
    println("apply() in object")
    "hello"
  }

  def apply(nums:Int*):ArrayBuffer[Int]={
    println("apply(nums:Int*)")
    val arr = new ArrayBuffer[Int]()
    nums.map(arr.append(_))
    arr
  }

}

// class ClassDemo5 是 object ClassDemo5 的伴生类
class ClassDemo5 {
  def apply() = println("apply in class")

  def say: Unit = {
    println("say Hello apply in class")
  }
}


object ClassDemo5_1 extends App{

  //调用无参数的apply()方法,创建ClassDemo5对象
  val c5 = ClassDemo5() //返回的就是
  println(c5.hashCode())

  /**
    * hello world
    * apply() in object
    */

  //调用可变参数的apply()方法,创建ClassDemo5对象
  val c5_1 = ClassDemo5(1,2,3,4,5) //返回的就是一个集合
  println(c5_1.toBuffer)
  println(c5_1.hashCode())
  /**
    * apply(nums:Int*)
    * ArrayBuffer(1, 2, 3, 4, 5)
    */

  //调用class中的apply方法
  val c5_2 = new ClassDemo5()
  c5_2.say

  /**
    * apply in class
    * say Hello apply in class
    */
}

3. 内部类

// 每个外部类的实例对象所持有的内部类都是不同的类

class Persion{
  class Student
  //val listBuffer = ListBuffer[Student]()

  //类型投影
  val listBuffer = ListBuffer[Persion#Student]()

  def getStudent:Student={
    new Student
  }
}
object ObjectDemo08 {
  def main(args: Array[String]): Unit = {
    val p1 = new Persion
    val s1 = p1.getStudent
    p1.listBuffer += s1

    val p2 = new Persion
    val s2 = p2.getStudent
    //因为外部类所持有的内部类不是相同的对象 所以不能放进到listBuffer中
    //1.可通过外部类的伴生对象扩大内部类的影响范围,因为对象是单例的所以是相同类型
    //2. 可以通过外部类的类型投影来扩大内部类的影响范围
    //p1.listBuffer += s2

  }
}

4. 匿名类

object ClassDemo6 {
  def main(args: Array[String]): Unit = {
    //这里并不是直接实例化对象 而是创建类的匿名类的实例对象
    new ClassDemo6 {
      override def sayHello(): Unit = {
        println("hello ClassDemo6 的匿名类")
      }
    }.sayHello()//hello persion04 的匿名子类

    //创建 Animal的匿名子类的实例对象
    val a = new ClassDemo6_1("小明",12){
      override def say: Unit = {
        println(s"hello ClassDemo6_1 匿名类 $name $age")
      }
    }
    a.say //hello 子类 小明 12
    val b = new ClassDemo6_1("小明",12)

    println(a.getClass) //
    println(b.getClass) //

    /**
      * hello ClassDemo6 的匿名类
      * hello ClassDemo6_1 匿名类 小明 12
      * class hnbian.scala.classobject.classpag.ClassDemo6$$anon$2
      * class hnbian.scala.classobject.classpag.ClassDemo6_1
      */
  }
}

5. 抽象类


object ClassDemo8 {

  def main(args: Array[String]): Unit = {
    val c8 = new ClassDemo8("xiaom",18)
    c8.sayHello //hello xiaom
  }
}

// 定义一个抽象类
abstract class ClassDemo8_1 {

  def sayHello // 抽象方法

  val name: String //定义常量,没有赋值
  var age: Int //定义变量,没有赋值

  val id = 1001 // 定义常量并初始化
  var number = 1001 // 定义变量并初始化

}

class ClassDemo8(val n:String,val a:Int) extends ClassDemo8_1{
  // 继承抽象类需要重写抽象类中的抽象方法
  override def sayHello: Unit = {
    println(s"hello $name")
  }

  override val name: String = n
  override var age: Int = a

  // id=1002 // 父类中的id使用val修饰的所以不能覆盖

  number = 1002
}

6. 样例类(case class)

scala 中被case修饰的类被称为样例类样例类和普通的类基本一致只是可以不使用new 关键字来创建对象

特点:

  • 新建类实例不用new
  • 自动定义好getXX方法,
  • 提供默认的toString()
  • 结合类继承可以通过模式匹配进行分解

6.1 样例类的定义


object ClassDemo7 {

  def main(args: Array[String]): Unit = {
    // 新建类实例不用new
    val c7 = ClassDemo7_1("xioam",12)
    //自动定义好getXX方法
    println(c7.name)
    println(c7.howOld())
    //提供默认的toString()
    println(c7.toString) //ClassDemo7_1(xioam,12)
    val c7_1 = ClassDemo7_1("xioam",12)
  }
}

// 定义一个样例类
case class ClassDemo7_1(name:String,age:Int){
  def howOld(): Int ={
    age
  }
}

6.2 样例类结合模式匹配


class Amimal
case class Cat(age: Int) extends Amimal
case class Dog(age: Int, name: String) extends Amimal

object ClassDemo7_1 {
  def main(args: Array[String]): Unit = {
    println(f(Cat(5))) //cat age =  5
    println(f(Dog(3, "dahuang"))) //dahuang age =  3
  }

  def f(t: Amimal) = t match {
    case Cat(x) => "cat age =  " + x
    case Dog(x, y) => y + " age =  " + x
  }
}

6.3 样例类代替tuple

case class Dog(age: Int, name: String) 

object ClassDemo7_2{
  def main(args: Array[String]): Unit = {
    val p1 = (20,"dahuang")

    println(p1._1)  //20
    println(p1._2)  //dahuang
    //好处是简洁,但无意义


    val p2 = Dog(20,"dahuang")
    println(p2.age)
    println(p2.name)
    // 好处是有名字,自说明,可读性强
  }
}

6.4 普通类与样例类的区别

在Scala中存在case class,它其实就是一个普通的class。但是它又和普通的class略有区别,如下:

  1. 初始化的时候可以不用new,当然你也可以加上,普通类一定需要加new;
scala> case class Student(name:String)
defined class Student

scala> val stu = Student("xiaoming")
stu: Student = Student(xiaoming)


scala> val stu2 = new Student("xiaohong")
stu2: Student = Student(xiaohong)
  1. toString的实现更漂亮;
scala> stu

res5: Student = Student(xiaoming)
  1. 默认实现了equals 和hashCode;
scala> val stu3 = new Student("xiaohong")
stu2: Student = Student(xiaohong)

scala> stu3 == stu2
res6: Boolean = true
  1. 默认是可以序列化的,也就是实现了Serializable ;
scala> class A
defined class A

scala> import java.io._
import java.io._

scala> val bos = new ByteArrayOutputStream 
bos: java.io.ByteArrayOutputStream =

scala> val oos = new ObjectOutputStream(bos)
oos: java.io.ObjectOutputStream = java.io.ObjectOutputStream@4c257aef

scala> oos.writeObject("xiaoming")

scala> val a = new A
a: A = $iwC$$iwC$A@71687b10

scala> oos.writeObject(a)
java.io.NotSerializableException: $iwC$$iwC$A
  1. 自动从scala.Product中继承一些函数;

  2. case class构造函数的参数是public级别的,我们可以直接访问;

scala> stu.name

res11: String = xiaoming
  1. 支持模式匹配;

其实感觉case class最重要的特性应该就是支持模式匹配。这也是我们定义case class的唯一理由,难怪Scala官方也说:It makes only sense to define case classes if pattern matching is used to decompose data structures. 。来看下面的例子:

object TermTest extends scala.App {
  def printTerm(term: Term) {
    term match {
      case Var(n) =>
        print(n)
      case Fun(x, b) =>
        print("^" + x + ".")
        printTerm(b)
      case App(f, v) =>
        print("(")
        printTerm(f)
        print(" ")
        printTerm(v)
        print(")")
    }
  }
  def isIdentityFun(term: Term): Boolean = term match {
    case Fun(x, Var(y)) if x == y => true
    case _ => false
  }
  val id = Fun("x", Var("x"))
  val t = Fun("x", Fun("y", App(Var("x"), Var("y"))))
  printTerm(t)
  println
  println(isIdentityFun(id))
  println(isIdentityFun(t))
}

7. 继承(extends)

  • 如果子类继承的父类是抽象类,那么要实现父类中所有未实现的方法与字段。
  • 当子类重写父类的非抽象字段时,父类中该字段不能用val 修饰否则会报异常。
  • 父类中抽象字段不能使用private修饰,因为抽象字段本身就是需要子类去实现的,如果添加private关键字,那么子类无法访问,则抛异常
  • 子类重写父类的非抽象方法时必须要添加 override 关键字,如果需要指明调用父类的方法,需要使用supper.say()方法
  • 子类继承父类后,在创建子类的实例对象时,会先初始化父类的构造函数,然后执行子类自己的构造函数
  • 如果子类的构造函数和父类的构造函数,接收的参数名字是一样的,那么子类的相同的参数名字就不能添加任何关键字进行修饰,否则scala会认为子类要重写父类的字段

7.1 继承示例


class Animal{
  println("Animal 的主构造器代码")
  def  say={
    println("hello")
  }
}

class Dog extends  Animal {
  println("Dog 的主构造器代码")
  override def say: Unit = {
    println("hello 汪汪")
  }
}
class Cat extends  Animal {
  println("Cat 的主构造器代码")
  override def say: Unit = {
    println("hello 喵喵")
  }
}

object ExtendsDemo1 {
  def main(args: Array[String]): Unit = {

    val dog = new Dog
    dog.say // hello 汪汪

    /**
      * Animal 的主构造器代码
      * Dog 的主构造器代码
      * hello 汪汪
      */

    val cat = new Cat
    cat.say // hello 喵喵

    /**
      * Animal 的主构造器代码
      * Cat 的主构造器代码
      * hello 喵喵
      */

  }
}
  • 继承抽象类
// 定义抽象类
abstract class ExtendsDemo3_1{
  //定义抽象字段
  val name:String
  //定义具体字段
  val age:Int = 18

  //定义抽象方法(必须在抽象类中)
  def sayHello(context:String)

  //也可以定义具体的方法
  def say(context:String)={
    println("say "+context)
  }
}

/**
  * 子类继承抽象类要实现父类中所有未实现的方法与字段
  */
class ExtendsDemo3 extends ExtendsDemo3_1{

  // 实现父类的name字段
  override val name: String = "小明"

  //子类重写父类的非抽象字段,父类中该字段不能用val 修饰否则会报异常
  override val age = 12

  override def sayHello(context: String): Unit = {
    println("say hello "+context)
  }
  //重写父类的非抽象方法必须要添加override,要指明调用父类的方法,需要使用supper.say()方法
  override def say(context: String) = {
    super.say(context)
  }

}

7.3 匿名类的继承


object ClassDemo6 {
  def main(args: Array[String]): Unit = {
    //这里并不是直接ClassDemo6 而是创建ClassDemo6的匿名子类的实例对象
    new ClassDemo6 {
      override def sayHello(): Unit = {
        println("hello ClassDemo6 的匿名子类")
      }
    }.sayHello()//hello ClassDemo6 的匿名子类

    //创建 ClassDemo6_1 的匿名子类的实例对象
    val a = new ClassDemo6_1("小明",12){
      override def say: Unit = {
        println(s"hello ClassDemo6_1 的匿名子类 $name $age")
      }
    }
    a.say // hello ClassDemo6_1 的匿名子类 小明 12
    val b = new ClassDemo6_1("小明",12)

    println(a.getClass)
    //class hnbian.scala.classobject.classpag.ClassDemo6$$anon$2

    println(b.getClass)
    //class hnbian.scala.classobject.classpag.ClassDemo6_1
  }
}



abstract class ClassDemo6{
  def sayHello()={
    println("helo")
  }
}

class ClassDemo6_1 (var name:String , var age:Int){
  def say={
    println(s"hello $name $age")
  }
}

7.2 isInstanceOf / asInstanceOf

  • isInstanceOf 用来判断实例对象是否是指定类及其子类, 那么意味着isInstanceOf 不能做精确判断
  • asInstanceOf 用来将对象转换成其他类型,在用asInstanceOf 之前,应该使用isInstanceOf 先做判断
// isInstanceOf   与   asInstanceOf
object ExtendsDemo1_1 {
  def main(args: Array[String]): Unit = {

    val dog:Any = new Dog
    //isInstanceOf 用来判断实例对象是否是指定类及其子类, 也就是说 isInstanceOf 不能做精确判断
    println(s"dog.isInstanceOf[Animal]=${dog.isInstanceOf[Animal]}") //dog.isInstanceOf[Animal]=true
    if(dog.isInstanceOf[Animal]){
      //asInstanceOf 用来将对象转换成其他类型,在用asInstanceOf 之前,应该使用isInstanceOf 先做判断
      val d = dog.asInstanceOf[Animal]
      d.say
    }

    //使用isInstanceOf不能做精确判断,如果要使用精确判断可以使用 getClass 和classOf配合起来使用
    val cat = new Cat
    println(cat.getClass == classOf[Cat])
  }
}

8. 特质(Trait)

  • 在scala中triat是一种特殊概念,triat的用途有很多,可以作为类似java中的interface接口使用
  • 类可以支持对triat的多重继承,继承的时候统一使用extends关键字,多继承时使用with关键字

8.1 特质的定义


// 定义一个trait
trait TraitDemo1_1{
  // trait 中的抽象方法
  def say(content:String):Unit

  //使用val修饰的非抽象字段,不能被重写
  val name:String = "小明"
  //使用var修饰的非抽象字段,可以被重写
  var name2:String = "小红"
  //抽象字段,不管是使用var还是val修饰的抽象字段都可以被重写
  val age:Int


}

// 定义一个trait
trait TraitDemo1_2{
  def sayHello(content:String):Unit
}

/**
  * 在这里trait 是作为接口使用,那么久必须实现trait的抽象方法
  * 继承父类的字段不会添加到类当中 继承的trait的字段会添加到该类中
  */
class TraitDemo1_3 extends TraitDemo1_1 with TraitDemo1_2 {

   //类实现triat的抽象方法,可以不使用override关键字,但实现非抽象方法一定要使用override
   def say(content: String): Unit = {
     println(content)
   }

  //类实现重写 triat的非抽象方法,一定要使用override
  override def sayHello(content: String): Unit = {
    println(s"hello $content")
  }

  // name 是用val 修饰,不能被重写
  // name = "xiaom"
  // name2 是用var 修饰,可以被重写
  name2 = "xiaoh"

  //可以重写trait 中的抽象字段,无论该字段用val 还是var修饰
  override val age: Int = 10
}

object TraitDemo1 {
  def main(args: Array[String]): Unit = {
    val t = new TraitDemo1_3
    t.say("hello") //hello
    t.sayHello("xiaom") //hello xiaom
  }
}

8.2 特质的构造函数

继承了类和Trait的构造机制如下

  1. 执行父类的构造代码块
  2. 执行Trait的构造代码块,如果继承多个Trait 那么从左往右执行Trait的构造代码块
  3. 如果多个Trait继承了同一个Trait,那么限制性父Trait的构造代码块,然后再执行子Trait的构造代码块
  4. 父Trait的构造代码块只会执行一次
  5. 最后执行自己的构造代码块,即子类的构造代码块最后执行

//trait 也有主构造代码块,不包含在任何方法中的代码就是trait的主构造器代码块
trait TraitDemo2_1{
  println("TraitDemo2_1 的主构造代码块")
}

trait TraitDemo2_2 extends TraitDemo2_1{
  println("TraitDemo2_2 的主构造代码块(extends trait)")
}

trait TraitDemo2_3 extends TraitDemo2_1{
  println("TraitDemo2_3 的主构造代码块(extends trait)")
}

class TraitDemo2_4{
  println("TraitDemo2_4 的主构造代码块(extends class)")
}
class TraitDemo2_5 extends TraitDemo2_4 with TraitDemo2_2 with TraitDemo2_3{
  println("TraitDemo2_5 的主构造代码块")
}
object TraitDemo2 {
  def main(args: Array[String]): Unit = {
    val s = new TraitDemo2_5
    /**
      TraitDemo2_4 的主构造代码块(extends class)
      TraitDemo2_1 的主构造代码块
      TraitDemo2_2 的主构造代码块(extends trait)
      TraitDemo2_3 的主构造代码块(extends trait)
      TraitDemo2_5 的主构造代码块
      */
  }
}

8.3 在实体类中混入特质


trait TraitDemo3_1{
  def say: Unit ={
    println("hello")
  }
}

//一个类继承了一个父类或trait,那么这个类拥有该父类或者trait的所有非私有方法
class TraitDemo3_2(var name:String,var age:Int)

object TraitDemo3 {
  def main(args: Array[String]): Unit = {
    val b1 = new TraitDemo3_2("小明",1)

    //为实例对象混入trait,那么该实例对象拥有trait的所有非私有方法
    val b2 = new TraitDemo3_2("小明",1) with TraitDemo3_1
    b2.say
  }
}

文章作者: hnbian
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hnbian !
评论
 上一篇
【scala系列】11、泛型、上下边界、协变、逆变 【scala系列】11、泛型、上下边界、协变、逆变
1. 泛型类 泛型类就是在类的声明中,定义一些泛型类型。然后类内部的字段或者方法就可以使用这些泛型类型 使用泛型类通常是需要对类中的某些成员(字段、方法中的参数或变量)进行统一的类型限制,这样可以保证程序更好的健壮性和稳定性 如果不使用泛型
2020-04-15
下一篇 
【scala系列】9、Scala模式匹配 【scala系列】9、Scala模式匹配
1. 模式匹配介绍模式匹配是检查某个值(value)是否匹配某一个模式的机制,一个成功的匹配同时会将匹配值解构为其组成部分。它是Java中的switch语句的升级版,同样可以用于替代一系列的 if/else 语句。 1.1 语法一个模式匹配
2020-04-08
  目录