背景
上一节介绍了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
关键字定义。它们可以包含抽象成员(没有实现的方法)和具体成员(有实现的方法)。
trait Greeter {def greet(): Unitdef farewell(): Unit = println("Goodbye!") // 具体成员
}
1.2. 实现Traits
一个类可以通过extends
关键字来实现一个或多个traits。如果一个trait包含抽象成员,那么实现该trait的类必须提供这些成员的具体实现。
class EnglishGreeter extends Greeter {def greet(): Unit = println("Hello!")
}
1.3. Traits的叠加(Stacking Traits)
一个类可以实现多个traits,从而组合多个行为。
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的类中必须被具体化。
trait Container {type Tdef content: T
}class IntContainer extends Container {type T = Intdef content: T = 42
}
1.5. 多重继承
在Scala中,一个类可以继承多个trait,这种多重继承的机制带来了强大的灵活性。当一个类继承了多个trait,而这些trait中包含有相同名称的方法时,可以通过不同的方式来组织方法的调用顺序和行为。
以下是一些常见的实现方式:
1.5.1. 线性化(Linearization)
当一个类实现多个traits时,tScala 的 trait 继承机制采用线性化(linearization)规则来决定方法的调用顺序。这个顺序是由 Scala 编译器根据 trait 的定义顺序和继承关系自动计算的。简单来说,最后定义的 trait 的方法会覆盖前面定义的同名方法。
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的类的类型。这可以通过自我类型注解来实现。
trait SelfTypeExample {this: SomeOtherTrait => // 这里指定了Self Typedef 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")
}
使用方式如下:
trait A {def sayHello(): Unit = println("Hello from A")
}trait B {this: A => // 指定 B 必须和 A 一起被混合override def sayHello(): Unit = {super.sayHello() // 调用 A 的 sayHelloprintln("Hello from B")}
}class C extends A with Bval c = new C()
c.sayHello() // 输出: Hello from A// Hello from B
1.5.2.2 使用辅助方法
如果 self-type
注解不适合,可以使用辅助方法来显式调用其他 trait 的方法。
trait A {def sayHelloA(): Unit = println("Hello from A")def sayHello(): Unit = sayHelloA()
}trait B {def sayHelloA(): Unit // 声明辅助方法override def sayHello(): Unit = {sayHelloA() // 调用 A 的 sayHelloAprintln("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. 使用默认参数和方法重载
有时可以通过参数化方法或重载方法来实现更复杂的行为,不过这更多依赖于具体的应用场景。
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 Bval c = new C()
c.sayHello() // 输出: B's Hello from A// Hello from B
通过以上方式,Scala 提供了丰富的机制来处理和调用多个 trait 中的同名方法,允许开发者根据需要灵活地组织方法调用顺序和行为。
1.6. Traits的早期定义(Early Definitions)
在Scala中,你可以使用早期定义来在构造器的初始化列表中引用traits中的方法。
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中的作用
- 在初始化变量的时候给定默认值
- 将函数赋值给变量的时候,参数占位,表示赋值的是函数本身
,而不是函数执行结果 - 在变长参数函数调用过程中,传入集合元素的时候告诉编译器传递的是集合中的元素
- 高阶函数调用过程中,参数占位
- 引入包的时候,表示包下的所有类和子包
- 模式匹配中对于不需要的数据可以使用_代替
以上,如有错误,请不吝指正!