天天看點

函數式程式設計與面向對象程式設計[4]:Scala的類型關聯Type Alias函數式程式設計與面向對象程式設計[4]:Scala的類型關聯Type Alias

函數式程式設計與面向對象程式設計[4]:Scala的類型關聯Type Alias

之劍 2016.5.4 23:55:19

<div id="category"></div>

類型關聯 Type Alias

type關鍵字

scala裡的類型,除了在定義class,trait,object時會産生類型,還可以通過type關鍵字來聲明類型。

type相當于聲明一個類型别名:

object TestMatrix extends App{  
  type IntList = List[Int]
  //接下來就可以這樣使用它:
  type Matrix = List[IntList]

  val m = Matrix( IntList(1,2,3),
                  IntList(1,2,3),
                  IntList(1,2,3))
}

           
scala> type IntList=List[Int]
defined type alias IntList


           

這種給類型一個别名的特性隻是一個小糖豆,不太甜,真正有趣的是給一類操作命名(聯想C#中定義delegate)。

比如這樣:

type PersonPredicate = Person => Boolean

           

接受一個Person,傳回一個Boolean,我們把這一類用來判斷一個人是否符合某個條件的操作統稱為PersonPredicate。

然後我們可以定義以下predicate:

val teenagerPred: PersonPredicate = person => person.age < 20
           

然後前面寫過的teenagers方法就可以這樣重新定義:

def teenagers(people: People): People = {
    people.filter(teenagerPred)
  }
           

按照這個思路下去,我們就可以開始composite functions了。比如說,我們跟人收稅,就可以這麼做:

type Tax = Person => Double
  val incomeTax: Tax = person => person.income * 5 / 100
  val kejuanzaTax: Tax = person => person.income * 20 / 100
  def giveMeYourMoney(p: Person) = {
    calculateTax(p, List(incomeTax, kejuanzaTax))
  }
  def calculateTax(person: Person, taxes: List[Tax]): Double = {
    taxes.foldLeft(0d) {
      (acc, curTax) => acc + curTax(person)
    }
  }

           

總結一下type alia這個糖衣:

一個類型的type alias,類似于這樣的:type t = x。編譯器将在所有使用到t的地方把t替換為x。

對于一種操作的type alias,編譯器将會根據參數清單和傳回值類型的不同将其替換為對應的Function0,Function1,Function2 …… 一直到Function22。

如果我們真的定義一個超過22個參數的操作會如何呢?

type twentyThree = (
      String, String, String, String,
      String, String, String, String,
      String, String, String, String,
      String, String, String, String,
      String, String, String, String,
      String, String, String
    ) => String
    
           

Scala編譯器會直接告訴我們: type Function23 is not a member of package scala

結構類型

結構類型(structural type)為靜态語言增加了部分動态特性,使得參數類型不再拘泥于某個已命名的類型,隻要參數中包含結構中聲明的方法或值即可。舉例來說,java裡對所有定義了close方法的抽象了一個Closable接口,然後再用Closable類型限制參數,而scala裡可以不要求參數必須繼承自Closable接口隻需要包含close方法;如下:

scala> def free( res: {def close():Unit} ) { 
            res.close 
        }

scala> free(new { def close()=println("closed") })
closed

           

也可以通過type在定義類型時,将其聲明為結構類型

scala> type X = { def close():Unit }
defined type alias X

scala> def free(res:X) = res.close

scala> free(new { def close()=println("closed") })
closed

           

上面傳入參數時,都是傳入一個實作close方法的匿名類,如果某個類/單例中實作了close方法,也可以直接傳入

scala> object A { def close() {println("A closed")} }

scala> free(A)
A closed

scala> class R { def close()=print("ok") }

scala> val r = new R

scala> free(r)
ok

           

結構類型還可以用在稍微複雜一點的“複合類型”中,比如:

scala> trait X1; trait X2;

scala> def test(x: X1 with X2 { def close():Unit } ) = x.close

           

上面聲明test方法參數的類型為:

X1 with X2 { def close():Unit }

           

表示參數需要符合特質X1和X2同時也要有定義close方法。

複合類型與with關鍵字

class A extends (B with C with D with E)

T1 with T2 with T3 …

           

這種形式的類型稱為複合類型(compound type)或者也叫交集類型(intersection type)。

跟結構類型類似,可以在一個方法裡聲明類型參數時使用複合類型:

scala> trait X1; trait X2;

scala> def test(x: X1 with X2) = {println("ok")}
test: (x: X1 with X2)Unit

scala> test(new X1 with X2)
ok

scala> object A extends X1 with X2

scala> test(A)
ok


           

也可以通過 type 聲明:

scala> type X = X1 with X2
defined type alias X

scala> def test(x:X) = println("OK")
test: (x: X)Unit

scala> class A extends X1 with X2

scala> val a = new A

scala> test(a)
OK



           

結構類型:定義方法或者表達式時,要求傳參具有某種行為,但又不想使用類,或者接口去限制,可以使用結構類型。

class Structural { def open()=print("A class instance Opened") }

    object Structural__Type {

      def main(args: Array[String]){
        init(new { def open()=println("Opened") }) //建立了一個匿名對象,實作open方法
        type X = { def open():Unit } //将右邊的表達式命名為一個别名
        def init(res:X) = res.open
        init(new { def open()=println("Opened again") })
        
        object A { def open() {println("A single object Opened")} } //建立的單例對象裡面也必須實作open方法
        init(A)
        
        val structural = new Structural
        init(structural)
        
      }

      def init( res: {def open():Unit} ) { //要求傳進來的res對象具有open方法,不限制類型
                res.open
            }
    }


           

Scala複合類型解析:

trait Compound_Type1;
    trait Compound_Type2;
    class Compound_Type extends Compound_Type1 with Compound_Type2
    object Compound_Type {
      def compound_Type(x: Compound_Type1 with Compound_Type2) = {println("Compound Type in global method")} //限制參數x即是Type1的類型,也是Type2的類型
      def main(args: Array[String]) {
        
        compound_Type(new Compound_Type1 with Compound_Type2) //匿名方式,結果:Compound Type in global method
        object compound_Type_oject extends Compound_Type1 with Compound_Type2 //object繼承方式,trait混入object對象中
        compound_Type(compound_Type_oject) //結果都一樣,Compound Type in global method
        
        type compound_Type_Alias = Compound_Type1 with Compound_Type2 //定義一個type别名
        def compound_Type_Local(x:compound_Type_Alias) = println("Compound Type in local method") //使用type别名進行限制
        val compound_Type_Class = new Compound_Type
        compound_Type_Local(compound_Type_Class) //結果:Compound Type in local method
        
        type Scala = Compound_Type1 with Compound_Type2 { def init():Unit } //type别名限制即是Type1,也是Type2,同時還要實作init方法
      }

    }

           

Infix Type

Infix Type:中值類型,允許帶有兩個參數的類型。

object Infix_Types {

      def main(args: Array[String]) {
        
        object Log { def >>:(data:String):Log.type = { println(data); Log } }
        "Hadoop" >>: "Spark" >>: Log //右結合,先列印出Spark,再列印出Hadoop
        
         val list = List()
         val newList = "A" :: "B" :: list //中值表達式
         println(newList)
        
        class Infix_Type[A,B] //中值類型是帶有兩個類型參數的類型
        val infix: Int Infix_Type String = null //此時A是Int,B為String,具體類型名寫在兩個類型中間
        val infix1: Infix_Type[Int, String] = null //和這種方式等價
        
        case class Cons(first:String,second:String) //中值類型
        val case_class = Cons("one", "two")
        case_class match { case "one" Cons "two" => println("Spark!!!") } //unapply
        
      }

    }



           

self-type

class Self {
        self => //self是this别名
        val tmp="Scala"
        def foo = self.tmp + this.tmp
    }
    trait S1
    class S2 { this:S1 => } //限定:執行個體化S2時,必須混入S1類型
    class S3 extends S2 with S1
    class s4 {this:{def init():Unit} =>} //也能用于結構類型限定

    trait T { this:S1 => } //也能用于trait
    object S4 extends T with S1
    object Self_Types {

      def main(args: Array[String]) {
        class Outer { outer =>
         val v1 = "Spark"
         class Inner {
         println(outer.v1)  //使用外部類的屬性
         }
        }
        val c = new S2 with S1 //執行個體化S2時必須混入S1類型
      }

    }



           
---


關于作者: 陳光劍,江蘇東海人, 号行走江湖一劍客,字之劍。程式員,詩人, 作家

http://universsky.github.io/​


---

<link rel="stylesheet" href="http://yandex.st/highlightjs/6.2/styles/googlecode.min.css">
  
<script src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
<script src="http://yandex.st/highlightjs/6.2/highlight.min.js"></script>
  
<script>hljs.initHighlightingOnLoad();</script>
<script type="text/javascript">
 $(document).ready(function(){
      $("h2,h3,h4,h5,h6").each(function(i,item){
        var tag = $(item).get(0).localName;
        $(item).attr("id","wow"+i);
        $("#category").append('<a class="new'+tag+'" href="#wow'+i+'">'+$(this).text()+'</a></br>');
        $(".newh2").css("margin-left",0);
        $(".newh3").css("margin-left",20);
        $(".newh4").css("margin-left",40);
        $(".newh5").css("margin-left",60);
        $(".newh6").css("margin-left",80);
      });
 });
</script>