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

有时候,不是因为你没有能力,也不是因为你缺少勇气,只是因为你付出的努力还太少,所以,成功便不会走向你。而你所需要做的,就是坚定你的梦想,你的目标,你的未来,然后以不达目的誓不罢休的那股劲,去付出你的努力,成功就会慢慢向你靠近。

导读:本篇文章讲解 Scala 高阶(十一):隐式转换和泛型,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文


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

创作时间:2022 年 8 月 8 日
博客主页: 🔍点此进入博客主页
—— 新时代的农民工 🙊
—— 换一种思维逻辑去看待这个世界 👀
今天是加入CSDN的第1256天。觉得有帮助麻烦👏点赞、🍀评论、❤️收藏



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

一、隐式转换

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

隐式函数

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

举个栗子:

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 关键字声明为隐式参数,调用该方法时,就可以传入该参数,编译器会在相应的作用域寻找符合条件的隐式值。

说明:

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

举个栗子:

    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 声明类,隐式类的非常强大,同样可以扩展类的功能,在集合中隐式类会发挥重要的作用。

说明:

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

举个栗子:

    // 隐式类
    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一样通过类型擦除/擦拭法来实现。
  • 定义时可以用+-表示协变和逆变,不加则是不变。

协变和逆变

语法:

class MyList[+T]{ } //协变
class MyList[-T]{ } //逆变
class MyList[T] //不变

举个栗子:

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自身或者父类。

举个栗子:

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有所帮助!!
在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/144858.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!