Scala由類的動態擴充套件想到型別類
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()
在控制檯測試下:
現在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)
在控制檯測試下:
我們想讓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()
同樣在控制檯測試一下:
看完這些例子可能覺得型別類的技巧好像很花哨但是卻不是很實用,其實型別類type class是從Haskell中傳來的概念,當然Scala社群也有ScalaZ和Cats等庫提供了豐富的TypeClass,通過這些庫可以方便的編寫我們得程式,當然這就是一個很大的課題了,留作以後再說明。
相關文章
- C# Enum列舉型別操作擴充套件類C#型別套件
- 分類擴充套件套件
- iOS分類(category)、類擴充套件(extension)、繼承的區別iOSGo套件繼承
- 由一次WCF專案的需求擴充套件想到的套件
- HttpContext擴充套件類HTTPContext套件
- weex ios擴充套件類的作用iOS套件
- 擴充套件類的三種方式(繼承,裝飾模式,動態代理)套件繼承模式
- Objective-C 類別(category)和擴充套件(Extension)ObjectGo套件
- dart系列之:dart類的擴充套件Dart套件
- Java-IoUtil擴充套件工具類Java套件
- C#中的擴充套件類的理解C#套件
- 大資料——Scala擴充套件大資料套件
- C# 擴充套件方法 借籤於 Objective-C 擴充套件類.C#套件Object
- iOS開發的分類和擴充套件iOS套件
- java資料型別擴充套件Java資料型別套件
- PowerToys外掛擴充套件(類似Alfred)套件Alfred
- Swift快速為類擴充套件屬性Swift套件
- 五個檢視擴充套件類 LL套件
- Scala型別類的小應用之Functor Foldable型別LDA
- 可擴充套件的TextView,ExpandableTextView與Scroller類的使用套件TextView
- 10.還不會擴充套件Dart中的類?套件Dart
- Azure Load Balancer : 動態擴充套件套件
- c# ExpandoObject動態擴充套件物件C#Object套件物件
- XML - Schema之資料型別擴充套件XML資料型別套件
- 一文看懂XR科技(擴充套件現實)——人類互動方式的終極形態套件
- 一個根據資料庫自動生成model類的擴充套件資料庫套件
- scala_繼承、型別判斷、抽象類、匿名內部類繼承型別抽象
- Laravel 執行時類的功能擴充套件的實現Laravel套件
- SpringBoot各類擴充套件點詳解Spring Boot套件
- 轉向Kotlin——列舉類和擴充套件Kotlin套件
- C#新特性:匿名類和擴充套件方法C#套件
- ES6各大資料型別的擴充套件大資料資料型別套件
- Ruby動態類別
- [提問交流]擴充套件裡的外掛如何歸類?套件
- [LAMP]php動態擴充套件模組安裝LAMPPHP套件
- 內容分類擴充套件性標籤設計套件
- Spring(11) - Introductions進行類擴充套件方法Spring套件
- Swift列舉,結構體,類,擴充套件,協議Swift結構體套件協議