Scala入门到精通——第二十节 类型参数(二)
本節主要內容
1. Ordering與Ordered特質
在介紹上下文界定之前,我們對Scala中的Ordering與Ordered之間的關聯與區別進行講解,先看Ordering、Ordered的類繼承層次體系:
通過上面兩個圖可以看到,Ordering混入了Java中的Comparator接口,而Ordered混入了java的Comparable接口,我們知道java中的Comparator是一個外部比較器,而Comparable則是一個內部比較器,例如:
而Comparable接口是用于內部比較,Person類自己實現Comparable接口,代碼如下
public class Person implements Comparable<Person>{private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}Person(String name){this.name=name;}@Overridepublic int compareTo(Person o) {if (this.getName().equalsIgnoreCase(o.getName())) {return 1;}else{return -1;}} }public class PersonComparable {public static void main(String[] args) {Person p1=new Person("搖擺少年夢");Person p2=new Person("搖擺少年夢2");//對象自身與其它對象比較,而不需要借助第三方if(p1.compareTo(p2)>0) {System.out.println(p1);}else{System.out.println(p2);}}}從上述代碼中可以看到Comparable與Comparator接口兩者的本質不同,因此Ordering混入了Comparator,Ordered混入了Comparator,它們之間的區別和Comparable與Comparator間的區別是相同的。這里先給出一個Ordered在scala中的用法,Ordering的用法,將在上下文界定的時候具體講解
case class Student(val name:String) extends Ordered[Student]{override def compare(that:Student):Int={if(this.name==that.name)1else-1} }//將類型參數定義為T<:Ordered[T] class Pair1[T<:Ordered[T]](val first:T,val second:T){//比較的時候直接使用<符號進行對象間的比較def smaller()={if(first < second) firstelsesecond} }object OrderedViewBound extends App{val p=new Pair1(Student("搖擺少年夢"),Student("搖擺少年夢2"))println(p.smaller) }2. 上下文界定
在第十七節中的類型參數(一)中,我們提到視圖界定可以跨越類繼承層次結構,其后面的原理是隱式轉換。本節要介紹的上下文界定采用隱式值來實現,上下文界定的類型參數形式為T:M的形式,其中M是一個泛型,這種形式要求存在一個M[T]類型的隱式值:
//PersonOrdering混入了Ordering,它與實現了Comparator接口的類的功能一致 class PersonOrdering extends Ordering[Person]{override def compare(x:Person, y:Person):Int={if(x.name>y.name) 1else -1} } case class Person(val name:String){println("正在構造對象:"+name) } //下面的代碼定義了一個上下文界定 //它的意思是在對應作用域中,必須存在一個類型為Ordering[T]的隱式值,該隱式值可以作用于內部的方法 class Pair[T:Ordering](val first:T,val second:T){//smaller方法中有一個隱式參數,該隱式參數類型為Ordering[T]def smaller(implicit ord:Ordering[T])={if(ord.compare(first, second)>0) firstelsesecond} }object ConextBound extends App{//定義一個隱式值,它的類型為Ordering[Person]implicit val p1=new PersonOrderingval p=new Pair(Person("123"),Person("456"))//不給函數指定參數,此時會查找一個隱式值,該隱式值類型為Ordering[Person],根據上下文界定的要求,該類型正好滿足要求//因此它會作為smaller的隱式參數傳入,從而調用ord.compare(first, second)方法進行比較println(p.smaller) }- 1
有時候也希望ord.compare(first, second)>0的比較形式可以寫為first > second這種直觀比較形式,此時可以省去smaller函數的隱式參數,并引入Ordering到Ordered的隱式轉換,代碼如下:
class PersonOrdering extends Ordering[Person]{override def compare(x:Person, y:Person):Int={if(x.name>y.name) 1else -1} } case class Person(val name:String){println("正在構造對象:"+name) }class Pair[T:Ordering](val first:T,val second:T){//引入odering到Ordered的隱式轉換//在查找作用域范圍內的Ordering[T]的隱式值//本例的話是implicit val p1=new PersonOrdering//編譯器看到比較方式是<的方式進行的時候,會自動進行//隱式轉換,轉換成Ordered,然后調用其中的<方法進行比較import Ordered.orderingToOrdered;def smaller={if(first<second) firstelsesecond} }object ConextBound extends App{implicit val p1=new PersonOrderingval p=new Pair(Person("123"),Person("456"))println(p.smaller) }3. 多重界定
多重界定具有多種形式,例如:
T:M:K //這意味著在作用域中必須存在M[T]、K[T]類型的隱式值
T<%M<%K //這意味著在作用域中必須存在T到M、T到K的隱式轉換
K>:T<:M //這意味著M是T類型的超類,K也是T類型的超類
…..
- 10
4. 類型約束
本節部分實驗來自:http://hongjiang.info/scala-type-contraints-and-specialized-methods/,感謝原作者的無私奉獻
前面講的類型變量界定、視圖界定都是將泛型限定在一定范圍內,而上下文界定則是將類型限定為某一類型。類型約束與下下文界定類型,只不過它是用于判斷類型測試,類型約束有以下兩種:
像上面的=:=符號很像一個操作符,但其實它是scala語言中的類,它們被定義在Predef當中
@implicitNotFound(msg = "Cannot prove that ${From} <:< ${To}.")sealed abstract class <:<[-From, +To] extends (From => To) with Serializable private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x }// not in the <:< companion object because it is also// intended to subsume identity (which is no longer implicit)implicit def conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A] @implicitNotFound(msg = "Cannot prove that ${From} =:= ${To}.")sealed abstract class =:=[From, To] extends (From => To) with Serializableprivate[this] final val singleton_=:= = new =:=[Any,Any] { def apply(x: Any): Any = x }object =:= {implicit def tpEquals[A]: A =:= A = singleton_=:=.asInstanceOf[A =:= A]}- 1
用法簡介:
object TypeConstraint extends App{def test[T](name:T)(implicit ev: T <:< java.io.Serializable)= { name } //正確,因為String類型屬于Serializable的子類 println(test("搖擺少年夢"))//錯誤,因為Int類型不屬于Seriablizable的子類println(test(134)) }那么問題來了,test方法定義了一個隱式參數,它的類型是T <:< java.io.Serializable,即只有T為java.io.Serializable的子類才滿足要求,但是我們在程序中并沒有指定隱式值,為什么這樣也是合法的呢?這是因為Predef中的conforms方法會為我們產生一個隱式值。
那類型約束<:<與類型變量界定<:有什么區別呢?下面給出的代碼似乎告訴我們它們之間好像也沒有什么區別:
- 1
但下面的代碼給我們演示的是類型約束<:<與類型變量界定<:之間的區別:
下面的代碼演示的是其在一般函數使用時的區別
下面的代碼給出的是其在隱式轉換時的區別
scala> def foo[B, A<:B] (a:A,b:B) = print("OK") foo: [B, A <: B](a: A, b: B)Unitscala> class A; class B; defined class A defined class Bscala> implicit def a2b(a:A) = new B warning: there were 1 feature warning(s); re-run with -feature for details a2b: (a: A)B //經過隱式轉換后,滿足要求 scala> foo(new A, new B) OK scala> def bar[A,B](a:A,b:B)(implicit ev: A<:<B) = print("OK") bar: [A, B](a: A, b: B)(implicit ev: <:<[A,B])Unit //可以看到,隱式轉換在<:<類型約束中不管用 scala> bar(new A, new B) <console>:12: error: Cannot prove that A <:< B.bar(new A, new B)^總結
以上是生活随笔為你收集整理的Scala入门到精通——第二十节 类型参数(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: geometry-api-java 学习
- 下一篇: Keras笔记(一)关于Keras模型