Scala-隐式转换和泛型

一、什么是隐式转换

隐式转换是由编程语言或者编程环境自动完成的,而不需要程序员显式地进行转换操作。在很多编程语言中,都会存在隐式转换的机制,以方便程序员进行操作。

这种转换通常发生在类型兼容的情况下,比如从一个较小范围的数据类型转换到一个较大范围的数据类型,或者从派生类对象转换到基类对象。

隐式转换允许将一个类型的值自动转换为另一个类型,以满足特定的操作或表达式的要求。

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

Tip:面试被问到了!

二、隐式函数

使用 implicit 关键字声明的函数称之为隐式函数

当想调用对象功能时,如果编译错误,那么编译器会尝试在当前作用域范围内查找能调用对应功能的转换规则,这个调用过程是由编译器完成的,所以称之为隐式转换。也称之为自动转换。

object Implicit {
  def main(args: Array[String]): Unit = {

    val new12 = new MyRichInt(12)
    println(new12.myMax(15))
    //上述方法每次在进行比较时需要创建对象调用方法

    //使用隐式函数
    implicit def convert(num:Int):MyRichInt = new MyRichInt(num)
    println(12.myMax(15)) //12.myMax(15) 编译不能通过,触发隐式转换

  }
}

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

我们使用隐式函数后发现,当编译不能通过时,编译器会尝试在当前作用域范围内查找能调用对应功能的转换规则。因此我们可以直接将类定义成隐式类,而用再多余的使用隐式函数。

三、隐式类

在 Scala2.10 后提供了隐式类,可以使用 implicit 声明类,隐式类的非常强大,同样可以扩展类的功能,在集合中隐式类会发挥重要的作用
隐式类说明:
(1)其所带的构造参数有且只能有一个
(2)隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是顶级

object Implicit {
  def main(args: Array[String]): Unit = {
    //隐式类
    implicit class MyRichInt2(val self:Int){
      //自定义比较大小的方法
      def myMax2(n:Int):Int = if(n<self) self else n
      def myMin2(n:Int):Int = if(n>self) n else self
    }
    println(12.myMax2(15))
  }
}

四、隐式参数

普通方法或者函数中的参数可以通过 implicit 关键字声明为隐式参数,调用该方法时,就可以传入该参数,编译器会在相应的作用域寻找符合条件的隐式值。
(1)同一个作用域中,相同类型的隐式值只能有一个
(2)编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关。
(3)隐式参数优先于默认参数
(4)如果方法中隐式参数定义了默认值,仍会使用作用域中的同类型的隐式参数

//隐式参数
    implicit val str: String = "aimyon"
    
    def sayHello()(implicit name:String): Unit ={
      println("hello"+name)
    }
    def sayHi()(implicit name:String="aimer"): Unit ={
      println("hello"+name)
    }
    //简便写法
    def sayHi2(): Unit ={
      println("hello"+implicitly[String])
    }
    sayHello() //helloaimyon
    sayHi()//helloaimyon
    

五、泛型

泛型的本质是类型参数化或参数化类型,在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型。(为了让一个集合中的所有元素都为同一类型)

class MyList[+T] //协变

class MyList[-T] //逆变

class MyList[T] //不变

协变:Son 是 Father 的子类,则 MyList[Son] 也作为 MyList[Father]的“子类”。
逆变:Son 是Father 的子类,则 MyList[Son]作为 MyList[Father]的“父类”。
不变:Son 是 Father 的子类,则 MyList[Father]与 MyList[Son]“无父子关系”。

object Generics {
  def main(args: Array[String]): Unit = {
    //创建Child对象,使用Parent进行引用
    val child:Parent = new Child
    //参数具有父子关系,但是其集合类型不具有父子关系,需要使用协变
//    val childList:MyCollection[Parent] = new MyCollection[Child]
    //使用逆变,SubChild为Child的子类,则 MyCollection[SubChild]作为 MyCollection[Child]的“父类”
    val childList:MyCollection[SubChild] = new MyCollection[Child]
  }
  class Parent{}
  class Child extends Parent{}
  class SubChild extends Child{}

  class MyCollection[-E]{}
}

5.1 泛型上下限

Class PersonList[T <: Person] //泛型上限

Class PersonList[T >: Person] //泛型下限

泛型的上下限的作用是对传入的泛型进行限定。
Class PersonList[T <: Person]
T可以是Person和Person的子类,不能为其它

5.2 上下文限定

def f[A : B](a: A) = println(a)
//等同于 def f[A] (a:A)(implicit arg:B[A])=println(a)

上下文限定是将泛型和隐式转换的结合产物,以下两者功能相同,使用上下文限定[A :Ordering]之后,方法内无法使用隐式参数名调用隐式参数,需要通过implicitly[Ordering[A]]获取隐式变量,如果此时无法查找到对应类型的隐式变量,会发生出错误。

def test[A<:Child](a:A): Unit ={
      println(a.getClass.getName)
    }

//    test[Child](new Child)
//只能够传入Child和Child的子类对象
    test[Child](new SubChild) //Study.Generics$Child
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aimyon_36

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值