前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Scala 高阶(十一):隐式转换和泛型

Scala 高阶(十一):隐式转换和泛型

作者头像
百思不得小赵
发布2022-12-01 14:49:14
5490
发布2022-12-01 14:49:14
举报
文章被收录于专栏:小赵Java总结小赵Java总结

大家好,我是百思不得小赵。

创作时间:2022 年 8 月 8 日 博客主页: 🔍点此进入博客主页 —— 新时代的农民工 🙊 —— 换一种思维逻辑去看待这个世界 👀


目录


在Scala中有一种特殊的机制,当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,用于将类型进行转换,实现二次编译。

一、隐式转换

  • 隐式转换可以在不需改任何代码的情况下,扩展某个类的功能。

隐式函数

  • 函数定义前加上implicit声明为隐式转换函数。
  • 当编译错误时,编译器会尝试在当前作用域范围查找能调用对应功能的转换规则,这个过程由编译器完成,称之为隐式转换或者自动转换。
代码语言:javascript
复制
implicit def convert(num: Int):MyRichInt = new MyRichInt(num)
  • 在当前上下文使用的时候,需要先定义在使用

举个栗子:

代码语言:javascript
复制
object Test_Implicit {
  def main(args: Array[String]): Unit = {
    // 隐式函数
    implicit def convert(num: Int):MyRichInt = new MyRichInt(num)
    println(12.myMax(16))
  }
}

// 自定义类
class MyRichInt(val self: Int) {
  // 自定义比较大小的方法
  def myMax(n: Int): Int = if (n < self) self else n

  def myMain(n: Int): Int = if (n < self) n else  self
}

隐式参数

普通方法或者函数中的参数可以通过 implicit 关键字声明为隐式参数,调用该方法时,就可以传入该参数,编译器会在相应的作用域寻找符合条件的隐式值。

说明:

  • 同一个作用域中,相同类型的隐式值只能有一个
  • 编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关。
  • 隐式参数优先于默认参数

举个栗子:

代码语言:javascript
复制
    implicit val str: String = "alice"
    implicit  val num: Int = 12

    def sayHello(implicit name: String) :Unit={
      println(" hello, " + name)
    }
    sayHello

    // 简便写法
    def hiAge(): Unit={
      println("hi, " + implicitly[Int])
    }
    hiAge()

隐式类

Scala2.10 后提供了隐式类,可以使用 implicit 声明类,隐式类的非常强大,同样可以扩展类的功能,在集合中隐式类会发挥重要的作用。

说明:

  • 隐式类所带的构造参数有且只能有一个
  • 隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是顶级的。

举个栗子:

代码语言:javascript
复制
    // 隐式类
    implicit class MyRichInt2(val self: Int) {
      // 自定义比较大小的方法 
      def myMax2(n: Int): Int = if (n < self) self else n

      def myMain2(n: Int): Int = if (n < self) n else  self
    }

    println(12.myMax2(16))

隐式机制

作用域:

  • 首先在当前代码作用域下查找隐式实体(隐式方法、隐式类、隐式对象)。
  • 如果第一条规查找隐式对象失败,会继续在隐式参数的类型的作用域中查找。
  • 类型的作用域是指该类型相关联的全部伴生对象以及该类型所在包的包对象。

说明:

  • 隐式函数和隐式类可以用于扩充类的功能,常用语比如内建类Int Double String这种。
  • 隐式参数相当于就是一种更高优先级的默认参数。用于多个函数需要同一个默认参数时,就不用每个函数定义时都写一次默认值了。

二、泛型

  • [TypeList],定义和使用都是。
  • 常用于集合类型中用于支持不同元素类型。
  • 和java一样通过类型擦除/擦拭法来实现。
  • 定义时可以用+-表示协变和逆变,不加则是不变。

协变和逆变

语法:

代码语言:javascript
复制
class MyList[+T]{ } //协变
class MyList[-T]{ } //逆变
class MyList[T] //不变

举个栗子:

代码语言:javascript
复制
object Test_Generics {
  def main(args: Array[String]): Unit = {
    // 协变和逆变
    val child: Parent = new Child
    val childList: MyCollection[Parent] =new MyCollection[Child]
//    val childList1: MyCollection[SubChild] =new MyCollection[Child]
  }
}

class Parent{}
class Child extends Parent{}
class SubChild extends Child{}

// 定义带泛型的集合类型
class MyCollection[+E] {}

说明: 比如SonFather是父子关系,Son是子类。

  • 协变(Covariance):MyList[Son]是MyList[Father]的子类,协同变化。
  • 逆变(Contravariance):MyList[Son]是MyList[Father]的父类,逆向变化。
  • 不变(Invariant):MyList[Father] MyList[Son]没有父子关系。

泛型上下限

  • 泛型上限:class MyList[T <: Type],可以传入Type自身或者子类。
  • 泛型下限:class MyList[T >: Type],可以传入Type自身或者父类。

举个栗子:

代码语言:javascript
复制
object Test03_Generics {
  def main(args: Array[String]): Unit = {
    // 上下限
    def test[A <:Child](a: A)={
      println(a.getClass.getName)
    }

    test[Child](new Child)
    test[SubChild](new SubChild)
    test[Child](new SubChild)
  }
}

class Parent{}
class Child extends Parent{}
class SubChild extends Child{}

// 定义带泛型的集合类型
class MyCollection[+E] {}

本次分享的内容到这里就结束了,整个Scala专栏的总结也随之告一段路,希望对大家学习Scala有所帮助!!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-08-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目录
  • 一、隐式转换
    • 隐式函数
      • 隐式参数
        • 隐式类
          • 隐式机制
          • 二、泛型
            • 协变和逆变
              • 泛型上下限
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档