Scala特证/特质【6.7 特质(Trait)】

(任意内容)

此处输入任意想输入的内容

6.7 特质(Trait)

  • Scala 语言中,采用特质 trait(特征)来代替接口的概念 ,也就是说,多个类具有相同的特质(特征)时,就可以将这个特质(特征)独立出来,采用关键字 trait 声明。
  • Scala 中的 trait 中即可以有抽象属性和方法,也可以有具体的属性和方法,一个类可
    以混入(mixin)多个特质。这种感觉类似于 Java 中的抽象类。
  • Scala 引入 trait 特征,第一可以替代 Java 的接口,第二个也是对单继承机制的一种
    补充。

Java 的接口

Java中的接口(Interface)是一种抽象类型,用于定义类应该具有的行为。接口可以看作是一组方法的集合,但是接口不能包含实现代码,只能定义方法的签名。类可以实现一个或多个接口,并提供接口中定义的方法的具体实现。

接口的定义使用interface关键字,语法如下:

public interface MyInterface {
    // 抽象方法
    void abstractMethod();

    // 默认方法
    default void defaultMethod() {
        // 方法实现
    }

    // 静态方法
    static void staticMethod() {
        // 方法实现
    }
}

在Java中,类通过implements关键字来实现接口:

public class MyClass implements MyInterface {
    // 实现接口中的抽象方法
    public void abstractMethod() {
        // 方法实现
    }
}

一个类可以实现多个接口,通过逗号分隔:

public class MyClass implements Interface1, Interface2 {
    // 实现接口中的抽象方法
    // ...
}

接口的作用

接口中的方法可以是抽象方法、默认方法(默认实现)和静态方法。抽象方法由实现类提供具体实现,而默认方法和静态方法可以直接在接口中提供实现代码。默认方法可以被实现类重写,静态方法则不能。

接口在Java中用于实现多态性和代码复用,它提供了一种契约机制,用于定义类的行为规范。通过实现接口,可以在不同的类中共享一组方法,提高代码的灵活性和可扩展性。接口在Java中被广泛应用于各种编程场景,例如回调机制、事件处理、依赖注入等。

接口在编程中起到了以下几个重要的作用:

  1. 定义契约:接口定义了一组方法的契约或规范,指定了类应该具备的行为。通过接口,可以明确地定义类应该提供哪些方法,以及这些方法应该如何被实现。接口提供了一种契约机制,帮助程序员在不同的类之间建立统一的行为规范。

  2. 实现多态:接口是实现多态性的关键。通过接口,可以实现类的多态性,使得不同的对象可以以统一的方式对待。通过接口的引用,可以引用不同实现类的对象,并调用它们实现的接口方法,实现了代码的灵活性和可扩展性。

  3. 代码复用:接口提供了一种代码复用的机制。通过接口,可以定义一组方法,并在多个类中实现这些方法。其他类可以实现相同的接口,从而复用接口中定义的方法。这种方式可以减少代码的重复编写,提高代码的可维护性和可复用性。

  4. 解耦和组件化:接口可以帮助解耦和实现组件化。通过接口,不同的模块或组件之间可以通过接口进行通信,而不需要了解具体的实现细节。这样可以降低模块之间的耦合度,提高系统的灵活性和可扩展性。

  5. 定义规范:接口可以被用作定义规范的工具。通过接口,可以定义一组方法和常量,并约束其他类去实现这些方法或使用这些常量。这样可以保证代码的一致性和可靠性。

总而言之,接口在编程中具有重要的作用,它定义了类的行为规范、实现多态性、实现代码复用、解耦和组件化,以及定义规范等。通过接口,可以提高代码的灵活性、可维护性和可扩展性,使得程序更加可靠和可重用。

抽象类的作用

抽象类在面向对象编程中起着重要的作用,它具有以下几个主要作用:

  1. 定义通用行为:抽象类可以定义一组通用的方法和属性,这些方法和属性可以被其子类继承和重写。抽象类可以提供一种通用的行为规范,让子类按照自己的需求进行具体实现。

  2. 实现代码复用:抽象类可以包含一些通用的方法的实现,这样子类就可以直接继承这些方法的实现,避免了代码的重复编写。通过抽象类的继承关系,可以实现代码的复用,提高代码的可维护性和可复用性。

  3. 实现多态性:抽象类可以作为父类,被其子类进行继承和扩展。通过抽象类的引用,可以引用不同子类的对象,实现多态性。这样可以在运行时根据实际对象的类型调用相应的方法,实现灵活的代码结构。

  4. 定义抽象方法:抽象类可以定义抽象方法,这些方法只有方法的声明而没有具体的实现。抽象方法必须由子类进行具体实现,通过抽象方法的定义,可以强制子类去实现特定的行为。

  5. 作为模板类:抽象类可以作为模板类,定义一些通用的方法和属性,提供给子类进行继承和使用。子类可以通过继承抽象类,并根据自己的需求进行具体实现和扩展,从而达到代码复用和模块化的目的。

总而言之,抽象类在面向对象编程中具有重要作用,它可以定义通用行为、实现代码复用、实现多态性、定义抽象方法和作为模板类。通过抽象类,可以提高代码的可维护性、可复用性和可扩展性,使得程序设计更加灵活和可靠。

6.7.1 特质声明

1)基本语法

trait 特质名 {
	trait 主体
}

2)案例实操

trait PersonTrait {
	 // 声明属性
	 var name:String = _
	 // 声明方法
	 def eat():Unit={
	 }
	 // 抽象属性
	 var age:Int
	 
	 // 抽象方法
	 def say():Unit
}
通过查看字节码,可以看到特质=抽象类+接口

6.7.2 特质基本语法

  • 一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素,所以在使用时,也采用了 extends 关键字,如果有多个特质或存在父类,那么需要采用 with关键字连接。
    在这里插入图片描述
    2)说明
    (1)类和特质的关系:使用继承的关系。
    (2)当一个类去继承特质时,第一个连接词是 extends,后面是 with。
    (3)如果一个类在同时继承特质和父类时,应当把父类写在 extends 后。

3)案例实操
(1)特质可以同时拥有抽象方法和具体方法
(2)一个类可以混入(mixin)多个特质
(3)所有的 Java 接口都可以当做 Scala 特质使用
(4)动态混入:可灵活的扩展类的功能

  • (4.1)动态混入:创建对象时混入 trait,而无需使类混入该 trait
  • (4.2)如果混入的 trait 中有未实现的方法,则需要实现
trait PersonTrait {
	 //(1)特质可以同时拥有抽象方法和具体方法
	 // 声明属性
	 var name: String = _
	 // 抽象属性
	 var age: Int
 // 声明方法
 def eat(): Unit = {
 	println("eat")
 }
 // 抽象方法
 def say(): Unit
	}
trait SexTrait {
	 var sex: String
}
//(2)一个类可以实现/继承多个特质
//(3)所有的 Java 接口都可以当做 Scala 特质使用
class Teacher extends PersonTrait with java.io.Serializable {
	 override def say(): Unit = {
 		println("say")
	 }
	 override var age: Int = _
	}
object TestTrait {
	 def main(args: Array[String]): Unit = {
	 val teacher = new Teacher
	 teacher.say()
	 teacher.eat()
	 //(4)动态混入:可灵活的扩展类的功能
	 val t2 = new Teacher with SexTrait {
	 override var sex: String = "男"
 }
 //调用混入 trait 的属性
	 println(t2.sex)
   }
}

6.7.3 特质叠加

  • 由于一个类可以混入(mixin)多个 trait,且 trait 中可以有具体的属性和方法,若混入的特质中具有相同的方法(方法名,参数列表,返回值均相同),必然会出现继承冲突问题。

冲突分为以下两种:

  • 第一种,一个类(Sub)混入的两个 trait(TraitA,TraitB)中具有相同的具体方法,且
    两个 trait 之间没有任何关系,解决这类冲突问题,直接在类(Sub)中重写冲突方法。
    在这里插入图片描述

  • 第二种,一个类(Sub)混入的两个 trait(TraitA,TraitB)中具有相同的具体方法,且
    两个 trait 继承自相同的 trait(TraitC),及所谓的“钻石问题”,解决这类冲突问题,Scala
    采用了特质叠加的策略。
    在这里插入图片描述
    所谓的特质叠加,就是将混入的多个 trait 中的冲突方法叠加起来,案例如下,

trait Ball {
 def describe(): String = {
	 "ball"
 	}
}
trait Color extends Ball {
	 override def describe(): String = {
 		"blue-" + super.describe()
 }
}
trait Category extends Ball {
  override def describe(): String = {
	 "foot-" + super.describe()
	 }
}
class MyBall extends Category with Color {
  override def describe(): String = {
	 "my ball is a " + super.describe()
 }
}
object TestTrait {
	 def main(args: Array[String]): Unit = {
		 println(new MyBall().describe())
	 }
}

结果如下:

在这里插入图片描述

6.7.4 特质叠加执行顺序

思考: 上述案例中的 super.describe()调用的是父 trait 中的方法吗?

  • 当一个类混入多个特质的时候,scala 会对所有的特质及其父特质按照一定的顺序进行
    排序,而此案例中的 super.describe()调用的实际上是排好序后的下一个特质中的 describe()
    方法。,排序规则如下:
    在这里插入图片描述

结论:
在这里插入图片描述
在这里插入图片描述

6.7.5 特质自身类型

1)说明

  • 自身类型可实现依赖注入的功能。

2)案例实操

class User(val name: String, val age: Int)
trait Dao {
 def insert(user: User) = {
 println("insert into database :" + user.name)
 }
}
trait APP {
 _: Dao =>
 def login(user: User): Unit = {
 println("login :" + user.name)
 insert(user)
 }
}
object MyApp extends APP with Dao {
 def main(args: Array[String]): Unit = {
 login(new User("bobo", 11))
 }
}

6.7.6 特质和抽象类的区别

1.优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。
2.如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,
而特质不行(有无参构造)。
3.特质(Trait)和抽象类(Abstract Class)是Scala语言中两种常见的代码组织方式,它们有一些区别和特点。

  1. 继承关系:特质和抽象类都可以被其他类继承。但是,一个类只能继承一个抽象类,而可以同时继承多个特质(多重继承)。

  2. 单继承 vs. 多重混入:由于Scala只支持单继承,所以通过继承一个抽象类,子类只能继承一个父类的特性。而通过混入特质,可以将多个特质的功能组合到一个类中,实现多重混入。

  3. 构造器:抽象类可以有构造器,而特质不能直接拥有构造器。特质可以通过在类中混入时传递参数的方式来影响类的行为。

  4. 抽象方法和具体方法:抽象类可以包含抽象方法和具体方法。抽象方法只有声明而没有具体实现,由子类实现。而特质可以包含抽象方法和具体方法,特质中的方法默认是抽象的,但也可以提供默认的实现。

  5. 实例化:抽象类不能直接实例化,只能被继承。而特质也不能直接实例化,但可以通过混入到具体类中来影响类的行为。

  6. 使用场景:抽象类通常用于定义一些共性的行为和状态,被子类继承和扩展。特质通常用于定义一些可复用的代码片段,可以被多个类混入,实现代码的复用和模块化。

总而言之,特质和抽象类在Scala中有不同的用途和特点。抽象类用于定义类的继承关系和共性行为,而特质用于实现代码的复用和模块化,可以多重混入到类中。根据具体的需求,可以选择合适的方式来组织代码结构。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>