函数式编程与面向对象编程[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>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容