1. 程式人生 > >Scala泛型、隱式轉換和隱式引數、檢視介紹、Scala中的上界、下界、結合柯里化進行隱式轉換

Scala泛型、隱式轉換和隱式引數、檢視介紹、Scala中的上界、下界、結合柯里化進行隱式轉換

二、泛型

一、Scala泛型
1. 泛型介紹
泛型用於指定方法或類可以接受任意型別引數,引數在實際使用時才被確定,泛型可以有效地增強程式的適用性,使用泛型可以使得類或方法具有更強的通用性。泛型的典型應用場景是集合及集合中的方法引數,可以說同java一樣,scala中泛型無處不在,具體可以檢視scala的api。
2. 泛型類、泛型方法
泛型類:指定類可以接受任意型別引數。
泛型方法:指定方法可以接受任意型別引數。
3. 案例
案例一:

package cn.toto.gen

/**
  * Created by toto on 2017/7/1.
  * 下面的意思就是表示只要是Comparable就可以傳遞,下面是類上定義的泛型
  */
/*class MrRight[T <:Comparable[T]] { //定義一個選擇方法,實現選擇的功能 def choose(first : T,second : T) : T = { if(first.compareTo(second) > 0) first else second } }*/ /** * 在類上定義泛型可以,當然,也可以在方法上定義泛型,定義的方式如下: */ class MrRight { def choose[T <: Comparable[T]](first:T,second:T) : T = { if(first.compareTo(second) > 0
) first else second } } object MrRight { def main(args: Array[String]): Unit = { // //如果是類上定義的泛型,通過下面的方式呼叫 // val mr = new MrRight[Boy] // val b1 = new Boy("laoduan",99) // val b2 = new Boy("laozhao",9999) // val boy = mr.choose(b1,b2) // //結果是:laozhao // println(boy.name) //如果是在方法上定義的泛型,可以使用下面的方式:
var mr = new MrRight val b1 = new Boy("laoduan",99) val b2 = new Boy("laozhao",9999) val boy = mr.choose[Boy](b1,b2) //結果同樣是:laozhao println(boy.name) } }

案例二:

package cn.toto.scala.enhance

/**
  * Created by toto on 2017/7/1.
  * 泛型類,泛型方法
  * 泛型用於指定方法或類可以接受任意型別引數
  * 引數在實際使用時才被確定
  * 泛型可以有效地增強程式的適用性
  * 使用泛型可以使用得類或方法具有更強的通用性
  */
//泛型類    F S T是型別的引數
class GenericClass[F,S,T](val f:F,val s:S,val t:T)
object GenericDemo {

  //泛型方法
  def getData[T](list:List[T]) = list(list.length/2)

  /**
    * 執行結果:
    * 26
    * 3
    */
  def main(args : Array[String]): Unit = {
    //例項化方式1
    val gc1 = new GenericClass("lisha",26,"beijing")
    //例項化方式2
    val gc2 = new GenericClass[String,String,Int]("lisha","beijing",26)

    println(getData(List("lisha",26,"beijing")))

    val t = getData[Int] _
    println(t(List(1,2,3,4,5)))
  }
}

二、 隱式轉換和隱式引數

1. 概念
隱式轉換和隱式引數是Scala中兩個非常強大的功能,利用隱式轉換和隱式引數,你可以提供優雅的類庫,對類庫的使用者隱匿掉那些枯燥乏味的細節。
2. 作用
隱式的對類的方法進行增強,豐富現有類庫的功能
3. 隱式轉換函式
是指那種以implicit關鍵字宣告的帶有單個引數的函式。

可以通過::implicit –v這個命令顯示所有做隱式轉換的類。
4. 隱式轉換例子

package cn.toto.impli

import java.io.File
import scala.io.Source

/**
  * 這個案例是為了一下讀出檔案中的內容,目的類似通過下面的read方法直接讀取出其中的內容:
  * var file = new File("E:\\wordcount\\input\\1.txt");
    var contents = file.read();
  *
  * Created by toto on 2017/7/1.
  * 隱式的增強File類的方法
  */
class RichFile(val from : File){
  //定義一個read方法,返回
  def read() : String = Source.fromFile(from.getPath).mkString
}

object RichFile {
  //隱式轉換方法(將原有的File型別轉成了file型別,在用的時候需要匯入相應的包)
  implicit def file2RichFile(from : File) = new RichFile(from)
}

object MainApp {
  def main(args: Array[String]): Unit = {
    val file = new File("E:\\wordcount\\input\\a.txt");
    //裝飾模式,顯示的增強(本來想實現:val contents = file.read(),但是卻使用RichFile的方式,所以是顯示的增強)
    val rf = new RichFile(file)
    val contents = rf.read();

    //目的是使用File的時候不知不覺的時候直接使用file.read()方法,所以這裡就要做隱式轉換

    //匯入隱式轉換,._將它下滿的所有的方法都匯入進去了。 導包的程式碼,可以
    import RichFile._
    //這裡沒有的read()方法的時候,它就到上面的這一行中的找帶有implicit的定義方法
    var content = new File("E:\\wordcount\\input\\a.txt").read
    println()

    //import RichFile.file2RichFile
    //println(file2RichFile(new File("E:\\wordcount\\input\\1.txt")).read)
  }
}
package cn.toto.scala

import java.awt.GridLayout

/**
  * Created by ZX on 2015/11/13.
  */
object ImplicitContext{
  //implicit def girl2Ordered(g : Girl) = new Ordered[Girl]{
  //  override def compare(that: Girl): Int = if (g.faceValue > that.faceValue) 1 else -1
  //}

  implicit object OrderingGirl extends Ordering[Girl]{
    override def compare(x: Girl, y: Girl): Int = if (x.faceValue > y.faceValue) 1 else -1
  }
}

class Girl(var name: String, var faceValue: Double){
  override def toString: String = s"name : $name, faveValue : $faceValue"
}

//class MissRight[T <% Ordered[T]](f: T, s: T){
//  def choose() = if(f > s) f else s
//}
//class MissRight[T](f: T, s: T){
//  def choose()(implicit ord: T => Ordered[T]) = if (f > s) f else s
//}

class MissRight[T: Ordering](val f: T, val s: T){
  def choose()(implicit ord: Ordering[T]) = if(ord.gt(f, s)) f else s
}

object MissRight {
  def main(args: Array[String]) {
    import ImplicitContext.OrderingGirl
    val g1 = new Girl("yuihatano", 99)
    val g2 = new Girl("jzmb", 98)
    val mr = new MissRight(g1, g2)
    val result = mr.choose()
    println(result)
  }
}

檢視介紹

隱含引數和方法也可以定義隱式轉換,稱作檢視。檢視的繫結從另一個角度看就是implicit的轉換。主要用在兩個場合:
當一個T型別的變數t要裝換成A型別時
當一個型別T的變數t無法擁有A型別的a方法或變數時
其實檢視的繫結是為了更方便的使用隱式裝換
用符號 <% 表示。

Scala中的上界、下界

1.上界、下界介紹
在指定泛型型別時,有時需要界定泛型型別的範圍,而不是接收任意型別。比如,要求某個泛型型別,必須是某個類的子類,這樣在程式中就可以放心的呼叫父類的方法,程式才能正常的使用與執行。此時,就可以使用上下邊界Bounds的特性;
Scala的上下邊界特性允許泛型型別是某個類的子類,或者是某個類的父類;

(1) U >: T

這是型別下界的定義,也就是U必須是型別T的父類(或本身,自己也可以認為是自己的父類)。

(2) S <: T

這是型別上界的定義,也就是S必須是型別T的子類(或本身,自己也可以認為是自己的子類)。

下面是檢視介紹和上界、下界的示例程式碼:
下面定義兩個類

package cn.toto.gen

/**
  * Created by toto on 2017/7/1.
  */
class Boy(val name : String,var faceVlue : Int) extends Comparable[Boy] {
  override def compareTo(o: Boy): Int = {
    this.faceVlue - o.faceVlue
  }
}
package cn.toto.gen

/**
  * Created by toto on 2017/7/1.
  */
class Girl(val name:String,var faceValue:Int) {

}
這裡寫程式碼片
package cn.toto.gen

import java.awt.GridLayout
import java.io.File

import cn.toto.impli.RichFile

/**
  * Created by ZhaoXing on 2016/8/18.
  */
object MyPreDef {
  //針對viewbound方式的
//  implicit def girl2Ordered(g : Girl) = new Ordered[Girl] {
//    override def compare(that: Girl): Int = {
//      g.faceValue - that.faceValue
//    }
//  }

  //針對Ordering型別的
  implicit object Girl2Ordering extends Ordering[Girl] {
    override def compare(x: Girl, y: Girl): Int = {
      x.faceValue - y.faceValue
    }
  }
}
package cn.toto.gen

/**
  * Created by toto on 2017/7/1.
  * 下面的意思就是表示只要是Comparable就可以傳遞,下面是類上定義的泛型
  */
/*class MrRight[T <:Comparable[T]] {
  //定義一個選擇方法,實現選擇的功能
  def choose(first : T,second : T) : T = {
    if(first.compareTo(second) > 0) first else second
  }
}*/

/**
  * 在類上定義泛型可以,當然,也可以在方法上定義泛型,定義的方式如下:
  */
class MrRight {
  def choose[T <: Comparable[T]](first:T,second:T) : T = {
    if(first.compareTo(second) > 0) first else second
  }
}

object MrRight {
  def main(args: Array[String]): Unit = {
//    //如果是類上定義的泛型,通過下面的方式呼叫
//    val mr = new MrRight[Boy]
//    val b1 = new Boy("laoduan",99)
//    val b2 = new Boy("laozhao",9999)
//    val boy = mr.choose(b1,b2)
//    //結果是:laozhao
//    println(boy.name)

    //如果是在方法上定義的泛型,可以使用下面的方式:
    var mr = new MrRight
    val b1 = new Boy("laoduan",99)
    val b2 = new Boy("laozhao",9999)
    val boy = mr.choose[Boy](b1,b2)
    //結果同樣是:laozhao
    println(boy.name)
  }
}
package cn.toto.gen

/**
  * Created by toto on 2017/7/1.
  */
//viewbound必須存在一個隱式轉換方法,檢視界定,必須有一個隱式轉換的方法
//class MissRight[T <% Ordered[T]] {
//  def choose(first:T,second : T) : T = {
//    //這裡的之所以可以用'>',是因為這裡的T可以轉換成Order的型別
//    if(first > second) first else second
//  }
//}

//如果使用上下文界定,必須存在一個隱式轉換的值,特點是使用Ording,但是不需要使用Ordering[T]
class MissRight[T : Ordering] {
  def select(first : T,second : T) : T = {
    //通過使用implicitly轉換型別
    val ord = implicitly[Ordering[T]]
    if(ord.gt(first,second)) first else second
  }
}

object MissRight {
  def main(args: Array[String]): Unit = {
    def main(args: Array[String]): Unit = {
      //這裡是使用viewbound方式的。
//      import MyPreDef._
//      val mr = new MissRight[Girl]
//      val g1 = new Girl("ab",90)
//      val g2 = new Girl("aaa",999)
//      val g = mr.choose(g1,g2)
//      println(g.name)

      //這裡是針對Order型別的
      import MyPreDef._
      val mr = new MissRight[Girl]
      val g1 = new Girl("ab",90)
      val g2 = new Girl("aaa",999)
      val g = mr.select(g1,g2)
      println(g.name)
    }
  }
}

結合柯里化進行隱式轉換:
程式碼如下:

package cn.com.toto.gen

/**
  * Created by toto on 2017/7/2.
  */
class Girl(val name : String,var faceValue:Int) {

}
package cn.com.toto.gen

import cn.toto.impli.RichFile

/**
  * Created by toto on 2017/7/2.
  */
object MyPreDef {
  implicit object Girl2Ordering extends Ordering[Girl] {
    override def compare(x: Girl, y: Girl): Int = {
      x.faceValue - y.faceValue
    }
  }
}
package cn.com.toto.gen

/**
  * Created by toto on 2017/7/2.
  */
class MissR[T] {

  //如果利用柯里化傳入一個隱式轉換函式,其作用和Viewbound是一樣的,都需要一個隱式轉換方法或函式
  def choose(f: T, s: T)(implicit ord: T => Ordered[T]) : T = {
    if (f > s) f else s
  }

  //如果利用柯里化傳入一個隱式的型別, 其作用和Contextbound是一樣的,都需要一個隱式轉換的值
  def select(f: T, s: T)(implicit ord: Ordering[T]) : T = {
    if(ord.gt(f, s)) f else s
  }

  def random(f: T, s: T)(implicit ord: Ordering[T]) : T = {
    import Ordered.orderingToOrdered
    if(f > s) f else s
  }
}

object MissR {
  def main(args : Array[String]): Unit = {
    import MyPreDef._
    val mr = new MissR[Girl]
    val g1 = new Girl("aaa", 80)
    val g2 = new Girl("bbb", 90)
    //val g = mr.choose(g1, g2)
    val g = mr.random(g1, g2)
    //執行結果是:bbb
    println(g.name)
  }
}