Scala学习之TRAITS和抽象类
Posted 顧棟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala学习之TRAITS和抽象类相关的知识,希望对你有一定的参考价值。
SCALA TRAITS AND ABSTRACT CLASSES
原文地址:https://docs.scala-lang.org/overviews/scala-book/traits-intro.html
Scala trait
是该语言的一个重要特性。 正如您将在接下来的课程中看到的,您可以像使用 Java interface
一样使用它们,也可以像使用具有实际方法的抽象类一样使用它们。 Scala 类还可以扩展和“混合”多个特征。
Scala 也有抽象类的概念,我们将展示何时应该使用抽象类而不是特征。
Using Scala Traits as Interfaces
原文地址:https://docs.scala-lang.org/overviews/scala-book/traits-interfaces.html
使用Scala trait
的一种方法类似于原始Java接口,在该接口中为某些功能定义所需的接口,但不实现任何行为。
A simple example
作为让我们开始的示例,假设您想编写一些代码来模拟狗和猫等任何有尾巴的动物。 在 Scala 中,您可以编写一个 trait 来启动建模过程,如下所示:
trait TailWagger {
def startTail(): Unit
def stopTail(): Unit
}
该代码声明了一个名为 TailWagger
的 trait,它声明任何扩展 TailWagger
的类都应该实现 startTail
和 stopTail
方法。 这两种方法都没有输入参数,也没有返回值。 此代码等效于此 Java 接口:
public interface TailWagger {
public void startTail();
public void stopTail();
}
Extending a trait
给出一個trait
trait TailWagger {
def startTail(): Unit
def stopTail(): Unit
}
您可以编写一个扩展 trait 并实现这些方法的类,如下所示:
class Dog extends TailWagger {
// the implemented methods
def startTail(): Unit = println("tail is wagging")
def stopTail(): Unit = println("tail is stopped")
}
如果您愿意,您也可以像这样编写这些方法:
class Dog extends TailWagger {
def startTail() = println("tail is wagging")
def stopTail() = println("tail is stopped")
}
请注意,在任何一种情况下,您都使用 extends 关键字来创建一个扩展单个 trait 的类:
class Dog extends TailWagger { ...
-------
如果您将 TailWagger
trait 和 Dog
类粘贴到 Scala REPL 中,您可以像这样测试代码:
scala> val d = new Dog
d: Dog = Dog@234e9716
scala> d.startTail
tail is wagging
scala> d.stopTail
tail is stopped
这演示了如何使用扩展 trait 的类来实现单个 Scala trait。
Extending multiple traits
Scala 允许您创建具有特征的非常模块化的代码。 例如,您可以将动物的属性分解为小的、合乎逻辑的、模块化的单元:
trait Speaker {
def speak(): String
}
trait TailWagger {
def startTail(): Unit
def stopTail(): Unit
}
trait Runner {
def startRunning(): Unit
def stopRunning(): Unit
}
一旦你有了这些小片段,你就可以通过扩展所有这些片段并实现必要的方法来创建一个 Dog 类:
class Dog extends Speaker with TailWagger with Runner {
// Speaker
def speak(): String = "Woof!"
// TailWagger
def startTail(): Unit = println("tail is wagging")
def stopTail(): Unit = println("tail is stopped")
// Runner
def startRunning(): Unit = println("I'm running")
def stopRunning(): Unit = println("Stopped running")
}
请注意 extends
和 with
是如何从多个 trait 创建一个类的:
class Dog extends Speaker with TailWagger with Runner {
-------
这段代码的要点:
- 使用
extends
来扩展第一个traits - 使用
with
来扩展后续的 trait
从你目前所见,Scala trait 就像 Java 接口一样工作。 但还有更多……
USING SCALA TRAITS LIKE ABSTRACT CLASSES
原文地址:https://docs.scala-lang.org/overviews/scala-book/traits-abstract-mixins.html
在上一章节中,我们展示了如何像原始 Java 接口一样使用 Scala 特征,但它们具有比这更多的功能。 您还可以向它们添加真正的工作方法,并将它们用作抽象类,或者更准确地说,用作 mixin。
A first example
为了证明这一点,这里有一个 Scala trait,它有一个名为 speak 的具体方法和一个名为 comeToMaster 的抽象方法:
trait Pet {
def speak = println("Yo") // concrete implementation of a speak method
def comeToMaster(): Unit // abstract
}
当一个类扩展一个 trait 时,必须实现每个抽象方法,所以这里有一个扩展 Pet 并定义了 comeToMaster
的类:
class Dog(name: String) extends Pet {
def comeToMaster(): Unit = println("Woo-hoo, I'm coming!")
}
除非你想覆盖 speak,否则不需要重新定义它,所以这是一个完美的 Scala 类。 现在你可以像这样创建一个新的 Dog:
val d = new Dog("Zeus")
然后你可以调用speak和comeToMaster。 这是它在 REPL 中的样子:
scala> val d = new Dog("Zeus")
d: Dog = Dog@4136cb25
scala> d.speak
Yo
scala> d.comeToMaster
Woo-hoo, I'm coming!
Overriding an implemented method
一个类也可以覆盖一个在 trait 中定义的方法。 下面是一个例子:
class Cat extends Pet {
// override 'speak'
override def speak(): Unit = println("meow")
def comeToMaster(): Unit = println("That's not gonna happen.")
}
REPL 展示了这是如何工作的:
scala> val c = new Cat
c: Cat = Cat@1953f27f
scala> c.speak
meow
scala> c.comeToMaster
That's not gonna happen.
Mixing in multiple traits that have behaviors
Scala trait 的一大优点是您可以将多个具有行为的 trait 混合到类中。 例如,这里有一个traits的组合,其中一个定义了一个抽象方法,另一个定义了具体的方法实现:
trait Speaker {
def speak(): String //abstract
}
trait TailWagger {
def startTail(): Unit = println("tail is wagging")
def stopTail(): Unit = println("tail is stopped")
}
trait Runner {
def startRunning(): Unit = println("I'm running")
def stopRunning(): Unit = println("Stopped running")
}
现在你可以创建一个 Dog 类来扩展所有这些特征,同时为 speak 方法提供行为:
class Dog(name: String) extends Speaker with TailWagger with Runner {
def speak(): String = "Woof!"
}
这是一个 Cat 类:
class Cat extends Speaker with TailWagger with Runner {
def speak(): String = "Meow"
override def startRunning(): Unit = println("Yeah ... I don't run")
override def stopRunning(): Unit = println("No need to stop")
}
REPL 表明这一切都像您期望的那样工作。 先Dog:
scala> d.speak
res0: String = Woof!
scala> d.startRunning
I'm running
scala> d.startTail
tail is wagging
接着Cat
scala> val c = new Cat
c: Cat = Cat@1b252afa
scala> c.speak
res1: String = Meow
scala> c.startRunning
Yeah ... I don't run
scala> c.startTail
tail is wagging
Mixing traits in on the fly
最后要注意的是,您可以对具有具体方法的 trait 做的一件非常有趣的事情是将它们动态地混合到类中。 例如,鉴于这些特征:
trait TailWagger {
def startTail(): Unit = println("tail is wagging")
def stopTail(): Unit = println("tail is stopped")
}
trait Runner {
def startRunning(): Unit = println("I'm running")
def stopRunning(): Unit = println("Stopped running")
}
and this Dog
class:
class Dog(name: String)
您可以在创建 Dog 实例时创建一个混合了这些特征的 Dog 实例:
val d = new Dog("Fido") with TailWagger with Runner
---------------------------
REPL 再次表明这是有效的:
scala> val d = new Dog("Fido") with TailWagger with Runner
d: Dog with TailWagger with Runner = $anon$1@50c8d274
scala> d.startTail
tail is wagging
scala> d.startRunning
I'm running
这个例子之所以有效,是因为 TailWagger
和 Runner
特征中的所有方法都已定义(它们不是抽象的)。
ABSTRACT CLASSES
原文地址:https://docs.scala-lang.org/overviews/scala-book/abstract-classes.html
Scala 也有一个抽象类的概念,类似于 Java 的抽象类。 但是因为traits 如此强大,你很少需要使用抽象类。 实际上,您只需要在以下情况下使用抽象类:
- 您想创建一个需要构造函数参数的基类
- 您的 Scala 代码将从 Java 代码中调用
Scala traits don’t allow constructor parameters
关于第一个原因,Scala trait 不允许构造函数参数
// this won’t compile
trait Animal(name: String)
因此,只要基本行为必须具有构造函数参数,就需要使用抽象类:
abstract class Animal(name: String)
但是,请注意一个类只能扩展一个抽象类。
When Scala code will be called from Java code
关于第二点——第二次需要使用抽象类时——因为 Java 对 Scala traits一无所知,如果你想从 Java 代码中调用你的 Scala 代码,你需要使用抽象类而不是trait。
Abstract class syntax
抽象类语法类似于特征语法。 例如,这里有一个名为Pet
的抽象类,它类似于我们在上一课中定义的“Pet”特征:
abstract class Pet (name: String) {
def speak(): Unit = println("Yo") // concrete implementation
def comeToMaster(): Unit // abstract method
}
鉴于抽象的Pet
类,您可以像这样定义一个Dog
类:
class Dog(name: String) extends Pet(name) {
override def speak() = println("Woof")
def comeToMaster() = println("Here I come!")
}
REPL 表明这一切都如宣传的那样:
scala> val d = new Dog("Rover")
d: Dog = Dog@51f1fe1c
scala> d.speak
Woof
scala> d.comeToMaster
Here I come!
Notice how name
was passed along
所有这些代码都类似于 Java,所以我们不会详细解释它。 需要注意的一件事是 name
构造函数参数是如何从 Dog
类构造函数传递到 Pet
构造函数的:
class Dog(name: String) extends Pet(name) {
请记住,Pet
被声明为将 name
作为构造函数参数:
abstract class Pet (name: String) { ...
因此,此示例展示了如何将构造函数参数从 Dog
类传递到 Pet
抽象类。 您可以使用以下代码验证这是否有效:
abstract class Pet (name: String) {
def speak: Unit = println(s"My name is $name")
}
class Dog(name: String) extends Pet(name)
val d = new Dog("Fido")
d.speak
我们鼓励您将该代码复制并粘贴到 REPL 中以确保它按预期工作,然后根据需要对其进行试验。
以上是关于Scala学习之TRAITS和抽象类的主要内容,如果未能解决你的问题,请参考以下文章