Scala 基礎(十六):泛型、類型約束-上界(Upper Bounds)/下界(lower bounds)、視圖界定(View bounds)、上下文界定(Context bounds)、協變、逆變

1 泛型

1)若是咱們要求函數的參數能夠接受任意類型。可使用泛型,這個類型能夠表明任意的數據類型。java

2)例如 List,在建立 List 時,能夠傳入整型、字符串、浮點數等等任意類型。那是由於 List 在 類定義時引用了泛型。好比在Java中:public interface List<E> extends Collection<E>spring

Scala泛型應用案例1ide

1)編寫一個Message類函數

2)能夠構建Int類型的Message,String類型的Message.學習

3)要求使用泛型來完成設計,(說明:不能使用Any)this

object GenericUse {
  def main(args: Array[String]): Unit = {
    val mes1 = new StrMessage[String]("10")
    println(mes1.get)    
    val mes2 = new IntMessage[Int](20)
    println(mes2.get)
  }}

// 在 Scala 定義泛型用[T], s 爲泛型的引用
abstract class Message[T](s: T) {
  def get: T = s
}
// 子類擴展的時候,約定了具體的類型
class StrMessage[String](msg: String) extends Message(msg)
class IntMessage[Int](msg: Int) extends Message(msg)

Scala泛型應用案例2spa

1)請設計一個EnglishClass (英語班級類),在建立EnglishClass的一個實例時,須要指定[ 班級開班季節(spring,autumn,summer,winter)、班級名稱、班級類型]scala

2)開班季節只能是指定的,班級名稱爲String, 班級類型是(字符串類型 "高級班", "初級班"..) 或者是 Int 類型(1, 2, 3 等)翻譯

3)請使用泛型來完成本案例.設計

// Scala 枚舉類型
object SeasonEm extends Enumeration {
  type SeasonEm = Value //自定義SeasonEm,是Value類型,這樣才能使用
  val spring, summer, winter, autumn = Value
}
object GenericUse2 {
  def main(args: Array[String]): Unit = {

    val class1 = new EnglishClass[SeasonEm, String, String](SeasonEm.spring, "001班", "高級班")
    println(class1.classSeason + " " + class1.className + " " + class1.classType)

    val class2 = new EnglishClass[SeasonEm, String, Int](SeasonEm.spring, "002班", 1)
    println(class2.classSeason + " " + class2.className + " " + class2.classType)
}}
// Scala 枚舉類型
object SeasonEm extends Enumeration {
  type SeasonEm = Value //自定義SeasonEm,是Value類型,這樣才能使用
  val spring, summer, winter, autumn = Value
}
// 定義一個泛型類
class EnglishClass[A, B, C](val classSeason: A, val className: B, val classType: C)

Scala泛型應用案例3

1)定義一個函數,能夠獲取各類類型的

2)List 的中間index的值 使用泛型完成

def getMidEle[A](l: List[A])={
    l(l.length/2)
}
object GenericUse3 {
  def main(args: Array[String]): Unit = {
    // 定義一個函數,能夠獲取各類類型的 List 的中間index的值
    val list1 = List("jack",100,"tom")
    val list2 = List(1.1,30,30,41)

    println(getMidEle(list1))

  }
  // 定義一個方法接收任意類型的 List 集合
  def getMidEle[A](l: List[A])={
    l(l.length/2)
  }
}

2 類型約束-上界(Upper Bounds)/下界(lower bounds)

上界(Upper Bounds)介紹和使用

在 scala 裏表示某個類型是 A 類型的子類型,也稱上界或上限,使用 <: 關鍵字,語法以下:

[T <: A]

//或用通配符:

[_ <: A]

scala中上界應用案例-要求

1)編寫一個通用的類,能夠進行Int之間、Float之間、等實現了Comparable接口的值直接的比較.//java.lang.Integer

2)分別使用傳統方法上界的方式來完成,體會上界使用的好處.

class CompareInt(n1: Int, n2: Int) {
  def greater = if(n1 > n2) n1 else n2
}
class CompareComm[T <: Comparable[T]](obj1: T, obj2: T) {
    def greater = if(obj1.compareTo(obj2) > 0) obj1 else obj2
}
//映射轉換 Predef.scala

scala中上界應用案例-代碼

object UpperBoundsDemo {
  def main(args: Array[String]): Unit = {
    //常規方式
    /*
    val compareInt = new CompareInt(-10, 2)
    println("res1=" + compareInt.greater)
    val compareFloat = new CompareFloat(-10.0f, -20.0f)
    println("res2=" + compareFloat.greater)*/
    /*val compareComm1 = new CompareComm(20, 30)
    println(compareComm1.greater)*/
    val compareComm2 = new CompareComm(Integer.valueOf(20), Integer.valueOf(30))
    println(compareComm2.greater)
    val compareComm3 =
      new CompareComm(java.lang.Float.valueOf(20.1f), java.lang.Float.valueOf(30.1f))
    println(compareComm3.greater)
    val compareComm4 = new CompareComm[java.lang.Float](201.9f, 30.1f)
    println(compareComm4.greater)
  }
}
/*class CompareInt(n1: Int, n2: Int) {
  def greater = if(n1 > n2) n1 else n2
}
class CompareFloat(n1: Float, n2: Float) {
  def greater = if(n1 > n2) n1 else n2
}*/
//使用上界的方式,能夠有更好的通用性
class CompareComm[T <: Comparable[T]](obj1: T, obj2: T) {
    def greater = if(obj1.compareTo(obj2) > 0) obj1 else obj2
}

下界(Lower Bounds)介紹和使用

在 scala 的下界或下限,使用 >: 關鍵字,語法以下:

[T >: A]

//或用通配符:

[_ >: A]

scala中下界應用實例

object LowerBoundsDemo {
  def main(args: Array[String]): Unit = {

    biophony(Seq(new Earth, new Earth)).map(_.sound())
    
    biophony(Seq(new Animal, new Animal)).map(_.sound())
    biophony(Seq(new Bird, new Bird)).map(_.sound())

    val res = biophony(Seq(new Bird))

    val res2 = biophony(Seq(new Object))
    val res3 = biophony(Seq(new Moon))
    println("\nres2=" + res2)
    println("\nres3=" + res2)

  }
  def biophony[T >: Animal](things: Seq[T]) = things
}
class Earth { //Earth 類
  def sound(){ //方法
    println("hello !")
  }
}
class Animal extends Earth{
  override def sound() ={ //重寫了Earth的方法sound()
    println("animal sound")
  }
}
class Bird extends Animal{
  override def sound()={ //將Animal的方法重寫
    print("bird sounds")
  }
}
class Moon {}

scala中下界的使用小結

def biophony[T >: Animal](things: Seq[T]) = things

1)對於下界,能夠傳入任意類型

2)傳入和Animal直系的,是Animal父類的仍是父類處理,是Animal子類的按照Animal處理

3)和Animal無關的,一概按照Object處理

4)也就是下界,能夠隨便傳,只是處理是方式不同

5)不能使用上界的思路來類推下界的含義

3 視圖界定(View bounds)

視圖界定基本介紹

<% 的意思是「view bounds」(視界),它比<:適用的範圍更廣,除了全部的子類型,還容許隱式轉換類型。

def method [A <% B](arglist): R = ... 等價於:

def method [A](arglist)(implicit viewAB: A => B): R = ... 或等價於:

implicit def conver(a:A): B = …

 

<% 除了方法使用以外,class 聲明類型參數時也可以使用: class A[T <% Int]

視圖界定應用案例1

object ViewBoundsDemo {
  def main(args: Array[String]): Unit = {
   //方式1    
    val compareComm1 = new CompareComm(20, 30)
    println(compareComm1.greater)
    //同時,也支持前面學習過的上界使用的各類方式,看後面代碼
  }
}
class CompareComm[T <% Comparable[T]](obj1: T, obj2: T) {
  def greater = if(obj1.compareTo(obj2) > 0) obj1 else obj2
}
object ViewBoundsDemo {
  def main(args: Array[String]): Unit = {
    val compareComm1 = new CompareComm(20, 30) //
    println(compareComm1.greater)

    val compareComm2 = new CompareComm(Integer.valueOf(20), Integer.valueOf(30))
    println(compareComm2.greater)

    val compareComm4 = new CompareComm[java.lang.Float](201.9f, 30.1f)
    println(compareComm4.greater)
    //上面的小數比較,在視圖界定的狀況下,就能夠這樣寫了
    val compareComm5 =
      new CompareComm(201.9f, 310.1f)
    println(compareComm5.greater)
  }
}

/**
  * <% 視圖界定 view bounds
  *   會發生隱式轉換
  */
class CompareComm[T <% Comparable[T]](obj1: T, obj2: T) {
  def greater = if(obj1.compareTo(obj2) > 0) obj1 else obj2
}

視圖界定應用案例2

說明: 使用視圖界定的方式,比較兩個Person對象的年齡大小。

val p1 = new Person("tom", 10)
val p2 = new Person("jack", 20)
val compareComm2 = new CompareComm2(p1, p2)
println(compareComm2.getter)



class Person(val name: String, val age: Int) extends Ordered[Person] {
  override def compare(that: Person): Int = this.age - that.age
  override def toString: String = this.name + "\t" + this.age}
class CompareComm2[T <% Ordered[T]](obj1: T, obj2: T) {
  def getter = if (obj1 > obj2) obj1 else obj2
  def geatter2 = if (obj1.compareTo(obj2) > 0) obj1 else obj2
}

視圖界定應用案例3

說明: 本身寫隱式轉換結合視圖界定的方式,比較兩個Person對象的年齡大小。

 // 隱式將Student -> Ordered[Person2]//放在object MyImplicit 中
  implicit def person22OrderedPerson2(person: Person2) = new Ordered[Person2]{
    override def compare(that: Person2): Int = person.age - that.age
  }

 val p1 = new Person2("tom", 110)
 val p2 = new Person2("jack", 20)
 import MyImplicit._
 val compareComm3 = new CompareComm2(p1, p2)
 println(compareComm3.geatter)

class Person2(val name: String, val age: Int)  {
  override def toString = this.name + "\t" + this.age
}
class CompareComm3[T <% Ordered[T]](obj1: T, obj2: T) {
  def geater = if (obj1 > obj2) obj1 else obj2
}

4 上下文界定(Context bounds)

與 view bounds 同樣 context bounds(上下文界定)也是隱式參數的語法糖。爲語法上的方便, 引入了」上下文界定」這個概念

 

 

object ContextBoundsDemo {
  implicit val personComparetor = new Ordering[Person] {
    override def compare(p1: Person, p2: Person): Int = 
       p1.age - p2.age
  }

  def main(args: Array[String]): Unit = {
    val p1 = new Person("mary", 30)
    val p2 = new Person("smith", 35)
    val compareComm4 = new CompareComm4(p1,p2)
    println(compareComm4.geatter)
    val compareComm5 = new CompareComm5(p1,p2)
    println(compareComm5.geatter)
    val compareComm6 = new CompareComm6(p1,p2)
    println(compareComm6.geatter)
  }}
//方式1
class CompareComm4[T: Ordering](obj1: T, obj2: T)(implicit comparetor: Ordering[T]) {
    def geatter = if (comparetor.compare(obj1, obj2) > 0) obj1 else obj2
}
//方式2,將隱式參數放到方法內
class CompareComm5[T: Ordering](o1: T, o2: T) {
    def geatter = {
        def f1(implicit cmptor: Ordering[T]) = cmptor.compare(o1, o2)
        if (f1 > 0) o1 else o2
    }}
//方式3,使用implicitly語法糖,最簡單(推薦使用)
class CompareComm6[T: Ordering](o1: T, o2: T) {
  def geatter = {
    //這句話就是會發生隱式轉換,獲取到隱式值 personComparetor
    val comparetor = implicitly[Ordering[T]]
    println("CompareComm6 comparetor" + comparetor.hashCode())
    if(comparetor.compare(o1, o2) > 0) o1 else o2
  }}
//一個普通的Person類
class Person(val name: String, val age: Int) {
  override def toString = this.name + "\t" + this.age
}

5 協變、逆變和不變

1)Scala的協變(+),逆變(-),協變covariant、逆變contravariant、不可變invariant

2)對於一個帶類型參數的類型,好比 List[T],若是對A及其子類型B,知足 List[B]也符合List[A]的子類型,那麼就稱爲covariance(協變) ,若是 List[A]是 List[B]的子類型,

即與原來的父子關係正反,則稱爲contravariance(逆變)。若是一個類型支持協變或逆變,則稱這個類型爲variance(翻譯爲可變的或變型),不然稱爲invariance(不可變的)

3)在Java裏,泛型類型都是invariant,好比 List<String> 並非 List<Object> 的子類型。而scala支持,能夠在定義類型時聲明(用加號表示爲協變,減號表示逆變),

如: trait List[+T] // 在類型定義時聲明爲協變這樣會把List[String]做爲List[Any]的子類型。

應用實例

在這裏引入關於這個符號的說明,在聲明Scala的泛型類型時,「+」表示協變,而「-」表示逆變 

C[+T]:若是A是B的子類,那麼C[A]是C[B]的子類,稱爲協變 

C[-T]:若是A是B的子類,那麼C[B]是C[A]的子類,稱爲逆變 

C[T]:不管A和B是什麼關係,C[A]和C[B]沒有從屬關係。稱爲不變.

val t: Temp[Super] = new Temp[Sub]("hello world1")
class Temp3[A](title: String) { //Temp3[+A] //Temp[-A]
  override def toString: String = {
    title
  }}
//支持協變
class Super
class Sub extends Super
相關文章
相關標籤/搜索