美文网首页
Scala由类的动态扩展想到类型类

Scala由类的动态扩展想到类型类

作者: 吐思圈 | 来源:发表于2018-01-06 21:06 被阅读0次

by 壮衣

在上一篇博文《Scala对JDBC的一些封装》中使用了一个黑魔法: 对java.sql.ResultSet类进行扩展为其添加了rows方法。我们称这种黑魔法为类的动态扩展,可以为已有的类添加新的方法。也许我们觉得给类添加新的方法,可以找到定义类的地方添加新的方法然后重新编译就好了,但是假如我们需要扩展类是Int、String或者JDK中自带类型或者第三方jar包中的类型我们便不好用这个方式扩展了。在Scala中可以通过隐式类来完成类的动态扩展,让我们来看一个简单的例子:我们想给String类添加一个sayHello方法,实现如下代码的效果:

  def sayHello(str: String): Unit = println(s"$str Hello")

回顾上一篇博文就知道我们可以定义一个隐式类,在隐式类中实现sayHello方法,然后在需要使用sayHello方法的地方引入隐式类,代码如下:

object StringUtils {

  implicit class StringOp(str: String) {

    def sayHello(): Unit = println(s"$str Hello")
  }
}

如上代码我们在隐式类StringOp中实现了sayHello方法,看看如何使用sayHello方法吧:

  import StringUtils._

  "zdx".sayHello()

在控制台测试下:


image.png

现在String类型可以调用sayHello函数了,假如我们想给Int类型也添加一个sayHello函数呢?这么看来我们得重新定义一个IntOp的隐式类并在隐式类中实现sayHello方法,还有别的方法吗?能否有一个sayHello方法对任何类型都适用?先试一下方法重载的方案:

  def sayHello(s: String): Unit = println(s"$s Hello")

  def sayHello(i: Int): Unit = println(s"Hello $i")

嗯,这个方案是ok得。但是能不能把两个sayHello变成一个呢? 再试一下模式匹配:

  def sayHello(a: Any): Unit = a match {
    case s: String => println(s"$s Hello")
    case i: Int => println(s"Hello $i")
    case _ => println(s"not support")
  }

方法变成一个了,但是假如我们需要新增类型就得重新更改sayHello方法,还有更好的方案吗?现在可以试一下类型类了:

trait Hello[A] {

  def hello(a: A): Unit
}

我们定义了一个Hello类型并在其中定义了一个hello方法,先不管hello方法的实现。我们来看一下如何定义一个对于任何类型都适用的sayHello的方法:

  def sayHello[A](a: A)(implicit s: Hello[A]): Unit = s.hello(a)

新的定义的sayHello方法可以接受任何类型入参a,也就是说sayHello方法适用任何类型。方法的具体实现其实是通过Hello[A]类型实现的,sayHello方法有一个隐式参数s是Hello[A],通过s的hello方法来实现sayHello方法。那么现在的问题就是我们需要实现Hello[A]类型,先看下Hello[String]类型和Hello[Int]类型如何实现的:

object Hello {

  implicit val stringHello: Hello[String] = new Hello[String] {

    override def hello(a: String): Unit = println(s"$a Hello")

  }

  implicit val intHello: Hello[Int] = new Hello[Int] {

    override def hello(a: Int): Unit = println(s"Hello $a")

  }
}

好了, 现在我们来调用下sayHello方法:

  import Hello._

  sayHello("zdx")

  sayHello(1)

在控制台测试下:


image.png

我们想让sayHello适用新的类型List[Int],使用类型类就不重新必修改sayHello方法了,只需要实现Hello[List[Int]]类型就好了:

  implicit val intListHello: Hello[List[Int]] = listSayHello(intHello)

  def listSayHello[A](s: Hello[A]) = new Hello[List[A]] {

    override def hello(a: List[A]): Unit = a.map(s.hello)

  }

对List[Int]使用sayHello函数:

  import Hello._

  sayHello(List(1, 2, 3))

到现在sayHello函数满足我们的要求,不过我们最开始想的是任何类型都可以调用sayHello方法,而不是写个sayHello方法施用到任何类型,这时候需要通过方法注入来实现:

trait HelloOp[A] {

  val h: Hello[A]

  val a: A

  def sayHello(): Unit = h.hello(a)
}


object HelloOp {

  implicit def toHelloOp[A](a1: A)(implicit h1: Hello[A]): HelloOp[A] = new HelloOp[A] {

    override val a: A = a1

    override val h: Hello[A] = h1

  }
}

现在通过toHelloOp这个隐式方法我们可以将任何类型A都转换成HelloOp[A]类型,然后调用sayHello方法。看下如何使用HelloOp :

  import HelloOp._

  "zdx".sayHello()

  1.sayHello()

  List(1, 2, 3).sayHello()

同样在控制台测试一下:


image.png

看完这些例子可能觉得类型类的技巧好像很花哨但是却不是很实用,其实类型类type class是从Haskell中传来的概念,当然Scala社区也有ScalaZ和Cats等库提供了丰富的TypeClass,通过这些库可以方便的编写我们得程序,当然这就是一个很大的课题了,留作以后再说明。

相关文章

  • Scala类型类的小应用之CSV Encoder

    by 壮衣 在上一篇博客《Scala由类的动态扩展想到类型类》中提到了类型类,写了一个类型类:Hello[A]并实...

  • Scala由类的动态扩展想到类型类

    by 壮衣 在上一篇博文《Scala对JDBC的一些封装》中使用了一个黑魔法: 对java.sql.ResultS...

  • Scala教程之:可扩展的scala

    Scala是扩展的,Scala提供了一种独特的语言机制来实现这种功能: 隐式类: 允许给已有的类型添加扩展方法 字...

  • Object类equals

     Object类是Java中所有类的始祖,每个类都是由Object类扩展而来。 在Java中,只有基本类型不是对象...

  • [译]Scala统一的类型

    在Scala中,所有值都有类型,包括数值和函数。下图说明了类的层次结构。 Scala类型的层次结构 Any是所有类...

  • Add CGFloat property to Category

    给扩展类添加CGFloat类型的属性 IOS中可以使用运行时动态给类添加属性,对象类型的属性添加方法大家都知道 但...

  • scala数据类型

    scala数据类型体系图如下 从上图可以得到以下结论: 在 scala 中有一个根类型 Any ,他是所有类的父类...

  • [译]Scala类

    Scala中的类用于创建对象。类中可以包含方法,值,变量,类型,对象,特征以及类,统称为成员。类型,对象和特征稍后...

  • Scala泛型

    泛型类是以类型作为参数,Scala类型参数放在方括号[]中,Java放在<>中 变型 Variance Scala...

  • Scala之旅(TOUR OF SCALA)——类(CLASSE

    类在 Scala 中是创建对象的蓝图。类可以包含方法,常量,变量,类型,对象,特征和类,它们被统一称为成员。类型成...

网友评论

      本文标题:Scala由类的动态扩展想到类型类

      本文链接:https://www.haomeiwen.com/subject/xrqynxtx.html