5. scala高阶之traits

背景

上一节介绍了scala的隐式转换,本文主要介绍scala的另外一个强大功能traits。

1. Traits

在Scala中,traits是一种非常强大的特性,它允许你定义抽象类型成员、具体方法实现、以及可以在多个类之间共享的行为。

可以将Trait作为接口来使用,此时的Triat就与Java中的接口非常类似,在triat中可以定义抽象方法,就与抽象类中的抽象方法一样,只要不给出方法的具体实现即可,类可以使用extends关键字继承trait。

注意,这里不是implement,而是extends,在scala中没有implement的概念,无论继承类还是trait,统一都是extends,类继承trait后,必须实现其中的抽象方法,实现时不需要使用override关键字。Scala不支持对类进行多继承,但是支持多重继承trait,使用with关键字即可

1.1. 定义Traits

Traits通过trait关键字定义。它们可以包含抽象成员(没有实现的方法)和具体成员(有实现的方法)。

scala 复制代码
trait Greeter {
  def greet(): Unit
  def farewell(): Unit = println("Goodbye!") // 具体成员
}

1.2. 实现Traits

一个类可以通过extends关键字来实现一个或多个traits。如果一个trait包含抽象成员,那么实现该trait的类必须提供这些成员的具体实现。

scala 复制代码
class EnglishGreeter extends Greeter {
  def greet(): Unit = println("Hello!")
}

1.3. Traits的叠加(Stacking Traits)

一个类可以实现多个traits,从而组合多个行为。

scala 复制代码
trait Logger {
  def log(message: String): Unit = println(s"Log: $message")
}

class EnglishGreeterWithLogging extends EnglishGreeter with Logger {
  override def greet(): Unit = {
    log("Greeting someone")
    super.greet()
  }
}

1.4. Traits中的抽象类型成员

Traits可以包含抽象类型成员,这些成员在实现该trait的类中必须被具体化。

scala 复制代码
trait Container {
  type T
  def content: T
}

class IntContainer extends Container {
  type T = Int
  def content: T = 42
}

1.5. 多重继承

在Scala中,一个类可以继承多个trait,这种多重继承的机制带来了强大的灵活性。当一个类继承了多个trait,而这些trait中包含有相同名称的方法时,可以通过不同的方式来组织方法的调用顺序和行为。

以下是一些常见的实现方式:

1.5.1. 线性化(Linearization)

当一个类实现多个traits时,tScala 的 trait 继承机制采用线性化(linearization)规则来决定方法的调用顺序。这个顺序是由 Scala 编译器根据 trait 的定义顺序和继承关系自动计算的。简单来说,最后定义的 trait 的方法会覆盖前面定义的同名方法。

scala 复制代码
trait A {
  def sayHello(): Unit = println("Hello from A")
}

trait B {
  def sayHello(): Unit = println("Hello from B")
}

class C extends A with B {
  // 默认情况下,C 的 sayHello 方法会调用 B 的 sayHello,因为 B 在 A 之后被混合进 C
}

val c = new C()
c.sayHello()  // 输出: Hello from B

1.5.2. 显式调用超trait的方法

如果需要在某个 trait 中调用另一个 trait 的同名方法,可以使用 super 关键字,不过这种用法有些限制,因为它直接依赖于线性化顺序。Scala 提供了更灵活的方式,通过 self-type 注解或者辅助方法来实现。

1.5.2.1 使用 self-type 注解

有时,你可能需要在trait中引用实现该trait的类的类型。这可以通过自我类型注解来实现。

scala 复制代码
trait SelfTypeExample {
  this: SomeOtherTrait => // 这里指定了Self Type
  def doSomething(): Unit
}

trait SomeOtherTrait {
  def anotherMethod(): Unit
}

class ExampleClass extends SomeOtherTrait with SelfTypeExample {
  def anotherMethod(): Unit = println("Another method called")
  def doSomething(): Unit = println("Doing something")
}

使用方式如下:

scala 复制代码
trait A {
  def sayHello(): Unit = println("Hello from A")
}

trait B {
  this: A =>  // 指定 B 必须和 A 一起被混合
  override def sayHello(): Unit = {
    super.sayHello()  // 调用 A 的 sayHello
    println("Hello from B")
  }
}

class C extends A with B

val c = new C()
c.sayHello()  // 输出: Hello from A
              //       Hello from B
1.5.2.2 使用辅助方法

如果 self-type 注解不适合,可以使用辅助方法来显式调用其他 trait 的方法。

scala 复制代码
trait A {
  def sayHelloA(): Unit = println("Hello from A")
  def sayHello(): Unit = sayHelloA()
}

trait B {
  def sayHelloA(): Unit  // 声明辅助方法
  override def sayHello(): Unit = {
    sayHelloA()  // 调用 A 的 sayHelloA
    println("Hello from B")
  }
}

class C extends A with B {
  override def sayHelloA(): Unit = super[A].sayHelloA()  // 显式调用 A 的 sayHelloA
}

val c = new C()
c.sayHello()  // 输出: Hello from A
              //       Hello from B

1.5.3. 使用默认参数和方法重载

有时可以通过参数化方法或重载方法来实现更复杂的行为,不过这更多依赖于具体的应用场景。

scala 复制代码
trait A {
  def sayHello(prefix: String = ""): Unit = println(s"$prefixHello from A")
}

trait B {
  override def sayHello(prefix: String = ""): Unit = {
    super.sayHello("B's ")
    println(s"$prefixHello from B")
  }
}

class C extends A with B

val c = new C()
c.sayHello()  // 输出: B's Hello from A
              //       Hello from B

通过以上方式,Scala 提供了丰富的机制来处理和调用多个 trait 中的同名方法,允许开发者根据需要灵活地组织方法调用顺序和行为。

1.6. Traits的早期定义(Early Definitions)

在Scala中,你可以使用早期定义来在构造器的初始化列表中引用traits中的方法。

scala 复制代码
trait EarlyDefTrait {
  def earlyMethod(): Unit
}

class EarlyDefClass extends {
  val x = earlyMethod() // 在这里调用earlyMethod
} with EarlyDefTrait {
  def earlyMethod(): Unit = println("Early method called")
}

总结

Scala的traits提供了一种灵活且强大的代码复用机制,使得你可以定义可组合的行为和抽象类型成员。通过traits,你可以创建出高度模块化和可维护的代码库。

2. 下划线在scala中的作用

  1. 在初始化变量的时候给定默认值
  2. 将函数赋值给变量的时候,参数占位,表示赋值的是函数本身
    ,而不是函数执行结果
  3. 在变长参数函数调用过程中,传入集合元素的时候告诉编译器传递的是集合中的元素
  4. 高阶函数调用过程中,参数占位
  5. 引入包的时候,表示包下的所有类和子包
  6. 模式匹配中对于不需要的数据可以使用_代替

以上,如有错误,请不吝指正!

相关推荐
像素猎人1 小时前
C语言按位取反【~】详解,含原码反码补码的0基础讲解【原码反码补码严格意义上来说属于计算机组成原理的范畴,不过这也是学好编程初级阶段的必修课】
c语言·开发语言
APItesterCris2 小时前
除了 Python,还有哪些语言可以调用淘宝 API?
大数据·开发语言·数据库·python
南棱笑笑生2 小时前
20250204在Ubuntu22.04下配置荣品的RK3566开发板的Android13的编译环境
java·开发语言
go54631584652 小时前
如何使用深度学习中的 Transformer 算法进行视频目标检测
开发语言·python
java1234_小锋2 小时前
Mybatis是如何进行分页的?
java·开发语言
来恩10033 小时前
C# 数组、索引器与集合介绍
开发语言·c#
游王子3 小时前
Python NumPy(12):NumPy 字节交换、NumPy 副本和视图、NumPy 矩阵库(Matrix)
开发语言·python·numpy
来恩10033 小时前
C# 委托与事件介绍
开发语言·c#
我是苏苏3 小时前
python开发:爬虫示例——GET和POST请求处理
开发语言·爬虫·python