2014-09-26 47 views
2

來源:Scala中MEAP V10「應用」如何在Scala中的伴隨對象(使用Trait)中工作?

功能編程在下面粘貼代碼

sealed trait List[+A] 
case object Nil extends List[Nothing] 
case class Cons[+A](head: A, tail: List[A]) extends List[A] 

object List { 
    def sum(ints: List[Int]): Int = ints match { 
    case Nil => 0 
    case Cons(x,xs) => x + sum(xs) 
} 
    def product(ds: List[Double]): Double = ds match { 
    case Nil => 1.0 
    case Cons(0.0, _) => 0.0 
    case Cons(x,xs) => x * product(xs) 
    } 
    def apply[A](as: A*): List[A] = { 

    if (as.isEmpty) Nil 
    else Cons(as.head, apply(as.tail: _*)) 
    } 
    val example = Cons(1, Cons(2, Cons(3, Nil))) 
} 

的缺點目的通過推測構造應用(),但該類型的簽名是不同的,如何階最終組裝Cons實例。

也同時存在未申請,雖然下面的代碼工作絕對沒問題,拆開列表到缺點(頭,尾)

object a{ 
    val x = List(1,2,3,4,5) match { 
    case Cons(x, Cons(2, Cons(4, _))) => x 
    case Nil => 42 
    case Cons(x, Cons(y, Cons(3, Cons(4, _)))) => x + y 
    case Cons(h, t) => h + List.sum(t) 
    case _ => 101 
    } 
} 

回答

5

一個Cons實例始終由Cons的構造函數構造。您可以直接調用它:

val myList = new Cons(1, new Cons(2, Nil)) // list containing the elements 1 and 2 

同伴對象上的apply方法是一個工廠方法,讓你構建一個更好的語法列表:

val myList = List(1, 2) // expanded to List.apply(1, 2) by the compiler ; same result as above 

沒有理由爲什麼他們會需要具有相同的類型簽名,因爲它們不需要以相同的方式調用。

至於爲什麼您可以使用模式匹配而不定義unapply方法:這是因爲您將NilCons定義爲案例類/對象。一個case類獲得許多免費的功能(由編譯器生成),包括unapply方法(但也包括equals,hashcodetoString)。


編輯:

構造Scala中:根據該意見的一些精度 在Scala中,每一類有一個默認的構造函數,它的參數會自動顯示在類領域 - 因此,默認構造函數不需要有它自己的方法體。例如,下面的Scala類:

class Foo1(bar: Int) 
class Foo2(val bar: Int) 

大致相當於下面的Java類:

public class Foo1 { 
    public Foo1(int bar) { 
     this.bar = bar; 
    } 
    private int bar; 
} 

public class Foo2 { 
    public Foo2(int bar) { 
     this.bar = bar; 
    } 
    private int bar; 
    public int getBar() { 
     return bar; 
    } 
} 

多態性: NilCons都擴展List。這意味着a Cons是一種List。所以,如果你創建的Consval myList = new Cons(1, Nil)一個實例,myListCons類型的對象......但它也:

  • ListAnyRef
  • ,因爲它的根類的所有引用類型的。 Scala中的所有類默認擴展爲AnyRef,因此List擴展爲AnyRef(因爲它沒有明確的extends子句)。
  • 類型Any,這是所有Scala類型的根。

下面的代碼使用您的List/Cons/Nil實現:

val myList = List(1, 2, 3) 
    // Cons(1,Cons(2,Cons(3,Nil))) => result of the toString method generated by the compiler because it's a case class 
myList.getClass.getName 
    // Cons => this is the concrete type of myList 
myList.isInstanceOf[Cons[_]] 
    // true => myList is a Cons 
myList.isInstanceOf[Nil.type] 
    // false 
myList.isInstanceOf[List[_]] 
    // true => but it's also a kind of List 
val foo: List[Int] = myList 
    //  => this is allowed 
myList.isInstanceOf[AnyRef] 
    // true 
myList.isInstanceOf[Any] 
    // true 

val anotherList = List() 
    // Nil 
anotherList.getClass.getName 
    // Nil$ => the name is mangled with a $ sign to differentiate the 'object Nil' in case you also declare a 'class Nil' 
anotherList.isInstanceOf[List[_]] 
    // true 
+0

但如何斯卡拉知道它必須創建(頭,尾)的列表時實施那裏只有在適用方法,我接受關於可變參數的句法糖的提及,以及不適用於案例類的方法。 – 2014-09-26 14:52:52

+0

我不是很確定我明白你的問題嗎?在編寫'MyObject(param)'時調用'MyObject.apply(param)'只是編譯器中實現的一種語言約定。 'apply'方法接受一個可變參數,在Scala中用一個'Seq'表示(而不是像Java中的數組),所以參數'as:A *'被用作參數'as:Seq [A]' 。 'apply'方法中的代碼然後使用這個'Seq'的公共方法來確定裏面的內容並調用正確的構造方法;如果它構造一個'Cons','apply'必須遞歸調用它來構造'Cons'的尾部。 – 2014-09-26 15:18:28

+0

對不起,如果調用'Cons(1,Cons(2,Cons(3,Nil)))'會返回一個'List(1,2,3,Nil)',這是怎麼發生的? apply方法碰巧做了一個構造List實例並返回的技巧,但是當我看到類型簽名中的差異時讓我感到困惑 – 2014-09-26 15:32:56

相關問題