1. 程式人生 > 其它 >scala型別引數05

scala型別引數05

技術標籤:# scalascala

所謂型別引數,其實就是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關鍵字修飾的相關變數,自動進行賦值。