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

这篇具有很好参考价值的文章主要介绍了【建议收藏】|3分钟让你学会Scala Trait 使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


**

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

**

Trait 是什么

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

Trait的作用

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

定义 Trait

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

trait Animal
class Dog extends Animal {
  def bark(): Unit = {
    println("Woof woof!")
  }
}
class Cat extends Animal {
  def meow(): Unit = {
    println("Meow meow!")
  }
}
trait Pet[A <: Animal] {
  def play(): Unit = {
    println("Playing with " + animalType)
  }
  def animalType: String
}
class PetDog extends Pet[Dog] {
  override def animalType: String = "Dog"
}
class PetCat extends Pet[Cat] {
  override def animalType: 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 边界来创建对象了。下面是一个简单的示例:

object Main extends App {
  val petDog = new PetDog()
  val petCat = new PetCat()
  petDog.play()  // 输出:Playing with Dog
  petCat.play()  // 输出:Playing with Cat
}

边界(Bounds)

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

上界(Upper Bounds)

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

同Trait 代码,这里不做赘述

下界(Lower Bounds)

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

下面是一个简单的例子:

trait Animal {
  def eat(): Unit
}
trait Cat extends Animal {
   def eat(): Unit = println("Eat Mouse")
}
trait Dog extends Animal {
   def eat(): Unit = println("Eat Shit")
}
trait Lactation[A >: Cat] extends Animal {
  def eat(): Unit = println("suckle")
}
class Persian extends Cat {
  override def eat(): Unit = println("Persian cats eat cat food")
}
class HaBa extends Dog {
  override def eat(): Unit = println("HaBa to eat bone")
}
object Main extends App {
  val persian: Animal = new Persian()
  val lactation: Animal = new Lactation[Cat] {}
  persian.eat() //输出: Persian cats eat cat food
  lactation.eat() // 输出: suckle
  val lactation1: Animal = new Lactation[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 对象或其子类的对象。

下面是一个简单的例子:

class Animal(val name: String)

class Cat(override val name: String) extends Animal(name)

class Dog(override val name: String) extends Animal(name)

class Cage[+T](val animal: T)

object CovarianceTest {
  def main(args: Array[String]): Unit = {
    val cat: Cat = new Cat("Tom")
    val catCage: Cage[Cat] = new Cage(cat)
    val animalCage: 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 对象或其父类的对象。

下面是一个简单的例子:

class Animal(val name: String)

class Cat(override val name: String) extends Animal(name)

class Dog(override val name: String) extends Animal(name)

class Feeder[-T] {
  def feed(animal: T): Unit = println(s"Feeding $animal")
}
object CovarianceTest {
  def main(args: Array[String]): Unit = {
      val cat: Cat = new Cat("Garfield")
      val animal: Animal = cat
      val feeder: Feeder[Cat] = new Feeder[Cat] // 创建一个类型为 Feeder[Cat] 的喂养器对象
      val animalFeeder: Feeder[Cat] = new Feeder[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边界、逆变和协变的介绍,希望对你有所帮助。文章来源地址https://www.toymoban.com/news/detail-603546.html

到了这里,关于【建议收藏】|3分钟让你学会Scala Trait 使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【JavaSE】保姆级教程|1万字+10张图入门到学会类与对象(建议收藏)

    🌱博主简介:大一计科生,努力学习Java中!热爱写博客~预备程序媛 📜所属专栏:爪洼岛冒险记【从小白到大佬之路】 ✈往期博文回顾: 【爪洼岛冒险记】第5站:多图解,超详细讲解Java中的数组、二维数组–建议收藏 🕵️‍♂️近期目标:成为千粉小博主。 🌺“再牛的程

    2023年04月19日
    浏览(44)
  • Spark SQL示例用法所有函数示例权威详解一【建议收藏】

    Spark中所有功能的入口点是 SparkSession 类。要创建一个基本的 SparkSession ,只需使用 SparkSession.builder() : 完整示例代码可在Spark存储库的“examples/src/main/scala/org/apache/spark/examples/sql/SparkSQLExample.scala”中找到。 在Spark 2.0中, SparkSession 提供了 对Hive功能的内置支持 ,包括 使用Hi

    2024年02月05日
    浏览(41)
  • 30分钟了解所有引擎组件,132个Unity 游戏引擎组件速通!【收藏 == 学会】

    🎬 博客主页:https://xiaoy.blog.csdn.net 🎥 本文由 呆呆敲代码的小Y 原创,首发于 CSDN 🙉 🎄 学习专栏推荐:Unity系统学习专栏 🌲 游戏制作专栏推荐:游戏制作 🌲Unity实战100例专栏推荐:Unity 实战100例 教程 🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📆 未来很长

    2024年02月11日
    浏览(58)
  • 十分钟掌握 Flink CDC,实现Mysql数据增量备份到Clickhouse [纯干货,建议收藏]

    Clickhouse的优点. 真正的面向列的 DBMS ClickHouse 是一个 DBMS,而不是一个单一的数据库。它允许在运行时创建表和数据库、加载数据和运行 查询,而无需重新配置和重新启动服务器。 数据压缩 一些面向列的 DBMS(InfiniDB CE 和 MonetDB)不使用数据压缩。但是,数据压缩确实提高了

    2024年04月14日
    浏览(36)
  • Scala的特质trait与java的interface接口的区别,以及Scala特质的自身类型和依赖注入

    Scala中的特质(trait)和Java中的接口(interface)在概念和使用上有一些区别: 默认实现:在Java中,接口只能定义方法的签名,而没有默认实现。而在Scala的特质中,除了可以定义方法签名外,还可以定义方法的具体实现。这样,在混入(mix in)特质的类中,可以直接使用特质

    2024年02月10日
    浏览(29)
  • ColorUI 全网最全使用文档(建议收藏)

    Color UI 我想大家都知晓吧,我就不过多阐述了,是 文晓港 大佬开发的一款适应于H5、微信小程序、安卓、ios、支付宝的高颜值,高度自定义的 Css 组件库.,属于出道即巅峰的史诗级大作,众所周知,万物皆可 Color UI,很多人用 Color UI 做了不少精美的项目,我也一样,在此再

    2024年02月14日
    浏览(37)
  • 五分钟学会使用FreeRTOS队列

    目录 何为队列? 示例代码 函数讲解 xQueueCreate xQueueSend xQueueReceive         在FreeRTOS中,队列是一种用于任务间通信的重要机制。队列可以用来在任务之间传递数据,实现数据的异步传输和共享。 队列的作用包括: 数据传输:任务可以通过将数据发送到队列中,供其他任

    2024年02月11日
    浏览(35)
  • spark底层为什么选择使用scala语言开发

    基于Scala的语言特性 集成性:Scala 是一种运行在 Java 虚拟机(JVM)上的静态类型编程语言,可以与 Java 代码无缝集成。由于 Spark 涉及到与大量 Java 生态系统的交互,例如 Hadoop、Hive 等,使用 Scala 可以方便地与这些组件进行集成和交互。 函数式编程支持:Scala 是一种面向函数

    2024年02月10日
    浏览(45)
  • 性能进阶:使用JMeter进行websocket测试【建议收藏】

    本次测试案例主要是分享如何使用JMeter进行websocket协议下的聊天接口性能测试。 包含websocket插件的下载安装、线程组及sampler的设置、csv参数化和组建分布式测试的方法、如何通过调整参数来获得发压机的最大并发数以及对测试过程的总结。 整篇文章只侧重介绍进行websocket测

    2024年02月08日
    浏览(46)
  • 简单使用Spark、Scala完成对天气数据的指标统计

    目录 一、前言   什么是Spark?   什么是Scala 二、数据准备(数据类型的转换) 三、Spark部分 1、使用Spark完成数据中的“风级”,“风向”、“天气情况”相关指标统计及筛选 四、Scala部分 1、使用Scala统计某月、全年的温差、平均气温以及最值等相关的指标 五、遇到的问题

    2024年02月03日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包