【建议收藏】|3分钟让你学会Scala Trait 使用

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
简介: Scala 是一种强大的静态类型编程语言,其中的 Trait 是一种重要的特性。Trait 可以被看作是一种包含方法和字段定义的模板,可以被其他类或 Trait 继承或混入。在本文中,我们将介绍 Scala Trait 的边界(Boundary)的概念,并展示如何使用它来限制 Trait 的使用范围。

欢迎关注公众号: 【857Hub】,带你玩转大数据

Trait 是什么

Scala 是一种强大的静态类型编程语言,其中的 Trait 是一种重要的特性。Trait 可以被看作是一种包含方法和字段定义的模板,可以被其他类或 Trait 继承或混入。在本文中,我们将介绍 Scala Trait 的边界(Boundary)的概念,并展示如何使用它来限制 Trait 的使用范围。


Trait的作用

Trait 可以用来限制 Trait 可以被哪些类或 Trait 继承或混入。通过定义边界,我们可以确保 Trait 只能被特定类型的类或 Trait 使用,从而提高代码的可读性和可维护性。


定义 Trait

在 Scala 中,我们可以使用 extends 关键字来定义 Trait 的边界。下面是一个简单的例子:

traitAnimalclassDogextendsAnimal {
defbark(): Unit= {
println("Woof woof!")
  }
}
classCatextendsAnimal {
defmeow(): Unit= {
println("Meow meow!")
  }
}
traitPet[A<:Animal] {
defplay(): Unit= {
println("Playing with "+animalType)
  }
defanimalType: String}
classPetDogextendsPet[Dog] {
overridedefanimalType: String="Dog"}
classPetCatextendsPet[Cat] {
overridedefanimalType: String="Cat"}

在上面的例子中,我们定义了一个名为 Animal 的 Trait,它是所有动物类的基类。然后我们定义了 Dog 和 Cat 两个类,它们都继承自 Animal。

接下来,我们定义了一个 Pet Trait,它的边界为 A <: Animal,表示它只能被继承自 Animal 的类使用。Pet Trait 中有一个 play 方法和一个 animalType 方法。

最后,我们定义了 PetDog 和 PetCat 两个类,它们分别继承自 Pet[Dog] 和 Pet[Cat]。这样,我们就限制了 PetDog 只能与 Dog 类一起使用,而 PetCat 只能与 Cat 类一起使用。


使用 Trait

现在我们可以使用定义好的 Trait 边界来创建对象了。下面是一个简单的示例:

objectMainextendsApp {
valpetDog=newPetDog()
valpetCat=newPetCat()
petDog.play()  // 输出:Playing with DogpetCat.play()  // 输出:Playing with Cat}

边界(Bounds)

Trait的边界指的是Trait可以被哪些类混入。Scala中有三种边界的写法:上界(Upper Bounds)、下界(Lower Bounds)和视图界(View Bounds)。


上界(Upper Bounds)

上界指定了Trait可以被拥有某个特定特质或超类的类混入。使用上界可以限制Trait的使用范围,确保只有满足条件的类才能混入该Trait。


> 同Trait 代码,这里不做赘述


下界(Lower Bounds)

下界指定了Trait可以被拥有某个特定子类的类混入。使用下界可以确保Trait只能被某个特定的子类混入。


下面是一个简单的例子:

traitAnimal {
defeat(): Unit}
traitCatextendsAnimal {
defeat(): Unit=println("Eat Mouse")
}
traitDogextendsAnimal {
defeat(): Unit=println("Eat Shit")
}
traitLactation[A>:Cat] extendsAnimal {
defeat(): Unit=println("suckle")
}
classPersianextendsCat {
overridedefeat(): Unit=println("Persian cats eat cat food")
}
classHaBaextendsDog {
overridedefeat(): Unit=println("HaBa to eat bone")
}
objectMainextendsApp {
valpersian: Animal=newPersian()
vallactation: Animal=newLactation[Cat] {}
persian.eat() //输出: Persian cats eat cat foodlactation.eat() // 输出: sucklevallactation1: Animal=newLactation[Dog] {}  //直接报错: Type Dog does not conform to lower bound Cat of type parameter A}

视图界(View Bounds)

视图界指定了Trait可以被某个特定隐式转换后的类混入。使用视图界可以实现对不同类型的隐式转换,从而扩展Trait的使用范围。


> 从Scala 2.10版本开始,视图界已被弃用,推荐使用上界(<:)或隐式参数来替代。


协变(Covariance)

协变是Trait的类型参数声明方式,用于指定Trait的泛型参数可以是Trait本身或者Trait的子类。


假设我们有一个简单的动物园应用程序,其中有一个 Cage 类表示动物笼子,同时有一个 Animal 类表示动物。为了让我们的应用程序更加灵活,我们希望能够创建一个 Cage[+T] 类型的容器类,该类中的类型参数 T 是协变的,这样就可以存放 Animal 对象或其子类的对象。


下面是一个简单的例子:

classAnimal(valname: String)
classCat(overridevalname: String) extendsAnimal(name)
classDog(overridevalname: String) extendsAnimal(name)
classCage[+T](valanimal: T)
objectCovarianceTest {
defmain(args: Array[String]): Unit= {
valcat: Cat=newCat("Tom")
valcatCage: Cage[Cat] =newCage(cat)
valanimalCage: Cage[Animal] =catCage// 协变println(animalCage.animal.name) // 输出 Tom  }
}

在这个代码中,我们定义了一个 Cage[+T] 类型的容器类,该类中的类型参数 T 是协变的。我们创建了一个 catCage 变量来存放 Cat 类型的对象,然后将其转换为一个 animalCage 变量,该变量可以存放 Animal 类型的对象。


在 main 方法中,我们首先创建了一个 Cat 类型的对象 cat,然后用 cat 创建了一个 Cage[Cat] 类型的对象 catCage。接下来,我们将 catCage 赋值给 animalCage,这是因为 Cage 类型是协变的,子类型的笼子可以赋值给父类型的笼子。最后,我们输出了 animalCage.animal.name,可以看到输出的是一个 Cat 类型的对象的名字。

逆变(Contravariance)

逆变是Trait中方法参数类型的一种特殊声明方式。逆变的方法参数类型可以是Trait的超类或者是Trait本身,但不能是Trait的子类。


假设我们希望为我们的动物园应用程序编写一个 Feeder 类来喂养动物。为了让 Feeder 类更加灵活,我们希望能够创建一个 Feeder[-T] 类型的喂养器类,该类中的类型参数 T 是逆变的,这样就可以接收 Animal 对象或其父类的对象。


下面是一个简单的例子:

classAnimal(valname: String)
classCat(overridevalname: String) extendsAnimal(name)
classDog(overridevalname: String) extendsAnimal(name)
classFeeder[-T] {
deffeed(animal: T): Unit=println(s"Feeding $animal")
}
objectCovarianceTest {
defmain(args: Array[String]): Unit= {
valcat: Cat=newCat("Garfield")
valanimal: Animal=catvalfeeder: Feeder[Cat] =newFeeder[Cat] // 创建一个类型为 Feeder[Cat] 的喂养器对象valanimalFeeder: Feeder[Cat] =newFeeder[Animal]
// val animalFeeder: Feeder[Animal] = feeder // 错误!不能将协变类型的 Feeder[Animal] 赋值给逆变类型的 Feeder[Cat]animalFeeder.feed(cat) // 输出 Feeding Cat(Garfield)animalFeeder.feed(cat) // 输出 Feeding Cat(Garfield)  }
}

在这个代码中,我们定义了一个 Feeder[-T] 类型的喂养器类,该类中的类型参数 T 是逆变的。我们想要创建一个 Feeder[Animal] 类型的喂养器对象,并将其赋值给一个类型为 Feeder[Cat] 的变量 feeder,这是不合法的,因为逆变只允许将父类型的对象赋值给子类型的变量。


# 总结

Scala中的Trait提供了灵活的边界、逆变和协变的特性,可以根据需求限制Trait的使用范围、参数类型和泛型参数类型。通过合理使用边界、逆变和协变,可以使代码更加灵活和可复用。


以上是关于Scala Trait边界、逆变和协变的介绍,希望对你有所帮助。

相关实践学习
基于Hologres轻松玩转一站式实时仓库
本场景介绍如何利用阿里云MaxCompute、实时计算Flink和交互式分析服务Hologres开发离线、实时数据融合分析的数据大屏应用。
Linux入门到精通
本套课程是从入门开始的Linux学习课程,适合初学者阅读。由浅入深案例丰富,通俗易懂。主要涉及基础的系统操作以及工作中常用的各种服务软件的应用、部署和优化。即使是零基础的学员,只要能够坚持把所有章节都学完,也一定会受益匪浅。
相关文章
|
6月前
|
分布式计算 Java Scala
Scala:面向对象、Object、抽象类、内部类、特质Trait(二)
Scala:面向对象、Object、抽象类、内部类、特质Trait(二)
92 0
|
Java Scala
scala面向对象编程之trait特质
特质就像是java的implement,是scala中代码复用的基础单元,它可以将方法和字段定义封装起来,然后添加到类中与类继承不一样的是,类继承要求每个类都只能继承一个超类,而一个类可以添加任意数量的特质。特质的定义和抽象类的定义很像,但它是使用trait关键字
117 0
scala面向对象编程之trait特质
|
大数据 编译器 Scala
大数据开发基础的编程语言的Scala的Trait
Scala是一种支持面向对象编程和函数式编程的编程语言,它提供了强大的Trait功能。本文将介绍Scala中Trait的概念和用法,帮助开发者更好地理解和应用这门语言。
96 0
|
设计模式 XML Java
基于Scala Trait的设计模式
基于Scala Trait的设计模式
|
Scala
Scala入门到精通——第十一节 Trait进阶
本节主要内容 trait构造顺序 trait与类的比较 提前定义与懒加载 trait扩展类 self type 1 trait构造顺序 在前一讲当中我们提到,对于不存在具体实现及字段的trait,它最终生成的字节码文件反编译后是等同于java中的接口,而对于存在具体实现及字段的trait,其字节码文件反编译后得到的java中的抽象类,它有着scala语言自己的实现方式
3554 0
|
Scala 网络架构 存储
|
Scala 索引 开发工具
|
Java Scala
scala 学习笔记(05) OOP(中)灵活的trait
trait -- 不仅仅只是接口! 接上回继续,scala是一个非常有想法的语言,从接口的设计上就可以发现它的与众不同。scala中与java的接口最接近的概念是trait,见下面的代码: package yjmyzz object App { def main(args...
946 0
|
Java 分布式计算 Spark
SCALA当的trait
不是特别懂,但感觉和RUBY当中的MIX-IN功能有几分相似,这又扯到了多重继承及JAVA当中的接口虚拟类了。。 package com.hengheng.scala class UseTrait { } trait Logger { def log(msg : Strin...
813 0