Scala入门到精通——第十七节 类型参数(一)
本節(jié)主要內(nèi)容
1. 類型變量界定(Type Variable Bound)
類型變量界定是指在泛型的基礎上,對泛型的范圍進行進一步的界定,從而縮下泛型的具體范圍,例如: //下面的類編譯通不過 //因為泛型T在編譯的時候不能確定其具體類型 //即并不是所有的類中都存在compareTo方法 class TypeVariableBound {def compare[T](first:T,second:T)={if (first.compareTo(second)>0) first else second} }object TypeVariableBound{def main(args: Array[String]): Unit = {val tvb=new TypeVariableBoundprintln(tvb.compare("A", "B")) } }如果在使TypeVariableBound類編譯通過,此時可以利用類型變量界定對泛型T進行界定,指明所有的泛型T都實現(xiàn)了Comparable接口,代碼如下:
class TypeVariableBound {//采用<:進行類型變量界定//該語法的意思是泛型T必須是實現(xiàn)了Comparable//接口的類型def compare[T <: Comparable[T]](first:T,second:T)={if (first.compareTo(second)>0) first else second} }object TypeVariableBound{def main(args: Array[String]): Unit = {val tvb=new TypeVariableBound//由于String類型實現(xiàn)了Comparable接口//下面這種使用方式是合法的println(tvb.compare("A", "B"))} }從上述代碼可以看到,compare方法中如果輸入的類型處于Comparable類對應繼承層次結(jié)構(gòu)中,則是合法的,否則的話編譯會報錯,例如:
//定義一個case class類Person case class Person(var name:String,var age:Int)class TypeVariableBound {def compare[T <: Comparable[T]](first:T,second:T)={if (first.compareTo(second)>0) first else second} }object TypeVariableBound{def main(args: Array[String]): Unit = {val tvb=new TypeVariableBoundprintln(tvb.compare("A", "B"))//下面這一行代碼會報錯,這是因為Person類并沒有//實現(xiàn)Comparable接口println(tvb.compare(Person("stephen",19), Person("john",20))) } }如果需要輸入Person類也合法的話,則Person類要實現(xiàn)Comparable接口,代碼如下:
//聲明Person類為case class且實現(xiàn)了Comparable接口 case class Person(var name:String,var age:Int) extends Comparable[Person]{def compareTo(o:Person):Int={if (this.age>o.age) 1 else if(this.age==o.age) 0else -1} }class TypeVariableBound {def compare[T <: Comparable[T]](first:T,second:T)={if (first.compareTo(second)>0) first else second} }object TypeVariableBound{def main(args: Array[String]): Unit = {val tvb=new TypeVariableBoundprintln(tvb.compare("A", "B"))//此時下面這條語句是合法的,因為//Person類實現(xiàn)了Comparable接口println(tvb.compare(Person("stephen",19), Person("john",20)))} }上面的類型變量界定都是作用于方法Compare上,類型變量界定除了作用于方法上外,還可以對類中的泛型進行范圍限定,例如:
//定義Student類為case class,且泛型T的類型變量界定為AnyVal //在創(chuàng)建類時,所有處于AnyVal類繼承層次結(jié)構(gòu)的類都是合法的 //如Int、Double等值類型 case class Student[S,T <: AnyVal](var name:S,var hight:T) object TypeVariableBound{def main(args: Array[String]): Unit = {//下面這條語句是不合法的,因為String類型不屬于//AnyVal類層次結(jié)構(gòu)// val S1=Student("john","170")//下面這兩條語句都是合法的,因為//Int,Long類型都是AnyValval S2=Student("john",170.0)val S3=Student("john",170L)} }從上面的例子中不難看出,類型變量界定可以對方法和類中的泛型進行范圍界定,這種界定建立在類繼承層次結(jié)構(gòu)的基礎之上,通過<:符號將泛型的范圍進行一步減少。
2. 視圖界定(View Bound)
上一節(jié)將的類型變量界定建立在類繼承層次結(jié)構(gòu)的基礎上,但有時候這種限定不能滿足實際要求,如果希望跨越類繼承層次結(jié)構(gòu)時,可以使用視圖界定來實現(xiàn)的,其后面的原理是通過隱式轉(zhuǎn)換(我們在下一講中會詳細講解什么是隱式轉(zhuǎn)換)來實現(xiàn)。視圖界定利用<%符號來實現(xiàn),在上一節(jié)中提到:
//使用的是類型變量界定 case class Student[T,S <: Comparable[S]](var name:T,var height:S) object ViewBound extends App{val s= Student("john","170")//下面這條語句不合法,這是因為//Int類型沒有實現(xiàn)Comparable接口val s2= Student("john",170) }上面這個問題可以通過視圖界定來解決,代碼如下:
//利用<%符號對泛型S進行限定 //它的意思是S可以是Comparable類繼承層次結(jié)構(gòu) //中實現(xiàn)了Comparable接口的類 //也可以是能夠經(jīng)過隱式轉(zhuǎn)換得到的類,該類實現(xiàn)了 //Comparable接口 case class Student[T,S <% Comparable[S]](var name:T,var height:S)object ViewBound extends App{val s= Student("john","170")//下面這條語句在視圖界定中是合法的//因為Int類型此時會隱工轉(zhuǎn)換為//RichInt類,而RichInt類屬于Comparable、//繼承層次結(jié)構(gòu)val s2= Student("john",170) }查看Scala API文檔可以看到
Int類會隱式轉(zhuǎn)換成RichInt類,RichInt并不是直接實現(xiàn)Comparable口,而是通過ScalaNumberProxy類將Comparable中的方法繼承過來:
ScalaNumberProxy混入了OrderedProxy,而OrderedProxy又混入了Ordered
trait Ordered混入了Comparable接口
可以看到,視圖界定比類型變量界定的限制要寬松一點,它不但可以是類繼承層次結(jié)構(gòu)中的類,也可以跨越類繼承層次結(jié)構(gòu),這后臺的實現(xiàn)方式是通過隱式轉(zhuǎn)換來進行的。
3. 上界(Upper Bound)與下界(Lower Bound)
下類代碼其實是類型參數(shù)中經(jīng)常提到的上界,這是因為它限定了繼承層次結(jié)構(gòu)中最頂層的類,例如T <: AnyVal表示泛型T的類型的最頂層類是AnyVal,所有輸入是AnyVal的子類都是合法的,其它的都是非法的,因為被稱為上界,有點像x<=3這樣的數(shù)學比較。
case class Student[S,T <: AnyVal](var name:S,var hight:T)- 1
除了上界之外,還有個非常重要的內(nèi)容就是下界,下界通過>:符號來標識,代碼如下:
class Pair1[T](val first:T,val second:T){//下界通過[R >: T]的意思是//泛型R的類型必須是T的超類def replaceFirst[R >: T](newFirst:R)= new Pair1[R](newFirst,second)override def toString()=first+"---"+second }//Book類 class Book(val name:String){override def toString()="name--"+name } //Book子類Ebook class Ebook(name:String) extends Book(name) //Book子類Pbook class Pbook(name:String) extends Book(name) //Pbook子類,WeridBook class WeirdBook(name:String) extends Pbook(name)object LowerBound extends App{val first = new Ebook("hello")val second = new Pbook("paper book")val p1 = new Pair1(first,second)println(p1)//scala> val p1 = new Pair1(first,second)//p1: Pair1[Book] = name--hello---name--paper book//Ebook,Pbook,最終得到的類是Pair1[Book]val newFirst = new Book("generic pBook")val p2 = p1.replaceFirst(newFirst)//p2: Pair1[Book] = name--generic pBook---name--paper bookprintln(p2)val weirdFirst:WeirdBook= new WeirdBook("generic pBook")val p3 = p1.replaceFirst(weirdFirst)//p3: Pair1[Book] = name--generic pBook---name--paper bookval p4 = new Pair1(second,second)//p4: Pair1[Pbook] = name--paper book---name--paper bookprintln(p4)val thirdBook=new Book("Super Books")val p5=p4.replaceFirst(thirdBook)println(p5)//下面這條語句會報錯//type mismatch; found : cn.scala.xtwy.lowerbound.Pair1[cn.scala.xtwy.lowerbound.Pbook] required: cn.scala.xtwy.lowerbound.Pbookval p6:Pbook=p4.replaceFirst(weirdFirst) }通過上述代碼發(fā)現(xiàn),如果newFirst的類型剛好是T的基類,R就直接是newFirst的類型。如果newFirst的類型不是T的基類,那R就會是T和newFirst的類型的共同基類。當限定返回變量類型時,例如val p6:Pbook=p4.replaceFirst(weirdFirst),由于p4為Pair1[Pbook],也即T為Pbook類型,而replaceFirst(weirdFirst)中的weirdFirst為Pbook的子類,違反了R>:T的下界限定,從而編譯出錯。從這里我們可以看到,下界的作用主要是保證類型安全
總結(jié)
以上是生活随笔為你收集整理的Scala入门到精通——第十七节 类型参数(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Scala入门到精通——第十六节 泛型与
- 下一篇: Keras笔记(一)一些基本概念