scala型別引數05
所謂型別引數,其實就是java中的泛型。
scala中的泛型和java中的泛型高度相似,也可以定義在類,方法上,成員變數上的泛型也需要沿用類的宣告,定義的是一般也是用一個大寫字母,但是泛型定義時需要使用[]包裹。
1. 泛型類
class XxxDao[T] {
def insert(t: T): Unit = {
}
}
trait XxxService[T] {
def save(t: T): Unit
}
class XxxServiceImpl extends XxxService[User] {
val dao = new XxxDao[ User]()
override def save(t: User): Unit = {
dao.insert(t)
}
}
class XxxController {
val service = new XxxServiceImpl
def register(t: User): Unit = {
service.save(t)
}
}
class User(name:String, age:Int) {
}
2.泛型上限,檢視界定
//scala版本的方法泛型
object _02GenericOps {
def main(args: Array[String]): Unit = {
val arr = Array[Integer](3, -1, 0, 5, -5, 7, 6)
println("排序前的陣列:" + arr.mkString("[", ", ", "]"))
insertSort(arr)
println("排序後的陣列:" + arr.mkString("[", ", ", "]"))
}
/*
scala中的泛型的限定語法格式
T <: 型別
java中的泛型限定語法格式
T extends 型別
*/
def insertSort[T <: Comparable[T]](array: Array[T]): Unit = {
for(x <- 1 until array.length) {
for(y <- 1 to x reverse) {
if(array(y).compareTo(array(y - 1)) < 0) {
swap(array, y, y - 1)
}
}
}
}
private def swap[T](arr: Array[T], y: Int, x: Int): Unit = {
val temp = arr(y)
arr(y) = arr(x)
arr(x) = temp
}
}
我們將java的版本修正為scala的版本,傳入Integer型別的陣列,完美解決問題,但是如果傳遞的是一個Int型別的陣列,編譯就會報錯:
這個錯誤說的是Int型別,不具備比較性
確實沒有體現出Comparable的特性,所以是不能直接進行比較,但是Int型別的資料,在scala中確確實實可以進行加減乘除等等運算,比較也是可以的,所以我們想要讓上述程式碼也能執行成功,就需要將泛型定義的修改為:
[T <: Comparable[T]] ==> [T <%: Comparable[T]]
3.泛型的協變和逆變
當然預設情況下,scala也只能支援=左右兩側泛型是一致的,但是為了程式更加的靈活和通用,scala也支援泛型出現繼承關係,所謂協變和逆變。
object _03GenericOps {
def main(args: Array[String]): Unit = {
val myList1:MyList[Person] = new MyList[Person]()
val myList2:MyList[Person] = new MyList[Student]()//泛型的協變
val myList3:MyList1[Student] = new MyList1[Person]()//泛型的逆變
}
}
class MyList[+T] {//泛型協變的定義
}
class MyList1[-T] {//泛型逆變的定義
}
class Person {}
class Student extends Person{}
4. 隱式轉換
scala提供的能夠將一種型別根據需要,自動轉化成其他型別的操作方式,進而可以讓原有的型別具備其沒有的功能,豐富現有api,而不會對原有的程式碼造成過多的汙染。
這一點是scala程式非常優秀的一點設計,是java等其它語言所不具備的能力。其實在前面的檢視界定案例中,檢視界定就採用的是隱式轉換將Int型別轉化成為了RichInt,而RichInt擴充套件了Comparable介面,自然可以進行比較了。
這樣,變數source就會在自己的作用域範圍內自動的搜尋是否有這樣的隱式轉換,如果有,則自動完成型別轉換或者加強。
通過隱式轉換函式實現
implicit def double2Int(d:Double):Int = d.toInt
val a:Int = 3.5
println("a=" + a)
5.利用隱士轉換豐富現有類庫的功能
利用隱士轉換豐富現有類庫的功能,舉例說明,通過java.io.File來讀取一個檔案中的內容,但是File沒有read的功能,如何能做到讓File也具備流的讀寫功能?
def main(args: Array[String]): Unit = {
val file = new File("E:/data/hello.txt")
file.read().foreach(println)
}
implicit def file2RichFile(file:File):RichFile = new RichFile(file)
//將file最後要轉化為RichFile,才能具備read的功能
class RichFile(file:File) {
def read() = Source.fromFile(file).getLines()
}
6.引入隱士轉換
當開發出一個隱式轉換函式時,該如何使用呢?
最簡單的其實就是,只要在要轉換的變數的作用域內能夠訪問的到,即可進行自動轉換,不需要人工干預。
在同一個檔案中定義的隱式轉換,不需要做任何的干預,但是如果要匯入其它類中定義的隱式轉換,要想在本類中使用,就必須要像導包一樣匯入。只要在其作用域到匯入,即可。同時這個匯入的隱式轉換因為和導包一樣,也不存在對原有類的結構發生入侵。
我們在匯入的時候,我們傾向於匯入到最靠近變數的部分。越靠近變數,其作用域範圍越小,影響越小。
object _03ImplicitOps {
def main(args: Array[String]): Unit = {
import _02ImplicitOps._
val file = new File("E:/data/hello.txt")
file.read().foreach(println)
}
}
7.隱式轉換引數
一個函式的引數被implicit修飾,稱該引數為隱式轉換引數,這個隱式轉換引數在傳遞的時候,可以傳,有時候也可以不用傳。
def main(args: Array[String]): Unit = {
val list = List(3, -3, 0, -8, 45, 4)
list.foreach(println)
println("---------------")
implicit val ord = new Ordering[Int](){
override def compare(x: Int, y: Int) = {
y.compareTo(x)
}
}
//顯示的傳遞隱式轉換引數
list.sorted(ord).foreach(println)
println("-------隱式傳遞---------")
//隱式傳遞隱式轉換引數
list.sorted.foreach(println)
}
說明:隱式轉換引數,可以顯示的指定,也可以隱式的指定,如果是隱式指定,就需要讓該變數在其作用域內訪問到被implicit關鍵字修飾的相關變數,自動進行賦值。