2010-09-20 74 views
6

我想知道爲什麼兩個類型參數(名爲「A」)具有相同的名稱(「A」)是允許按照下面的例子。我知道這是一個POOR命名的類型參數,不要這樣做。兩個同名的類型參數

(我的猜測是,它們是在一個上的不同範圍的水平,例如類水平和功能電平,並且編譯器使用某種名稱重整的)

class MyTest[A](){ 
    type MyType = A 

    def checkString[A](value:A, x:MyType):A = { 
     value match { 
     case x:String => println("Value is a String") 
     case _ => println("Value is not a String") 
     } 

     x match { 
      case x:String => println("x is a String") 
      case _ => println("x is not a String") 
     } 

     value 
    } 
} 

實施例輸出從2.8.0

scala> val test = new MyTest[Int] 
test: MyTest[Int] = [email protected] 

scala> test.checkString("String",1) 
Value is a String 
x is not a String 
res7: java.lang.String = String 

scala> test.checkString(1,1) 
Value is not a String 
x is not a String 
res8: Int = 1 
+1

由於你的格式化,這個問題變得更加難以閱讀--def塊和它下面的代碼的右括號應該被縮進。 – Submonoid 2010-09-20 13:59:43

回答

8

Scala中的嵌套範圍可以自由地映射彼此的符號表。類型不是唯一可以做到的事情。例如:

class X[A](a: A) { 
    def X[A](a: A) { 
    if (a==this.a) { 
     val X = Some(this.a) 
     X match { 
     case Some(a) => "Confused much yet?" 
     case _ => "Just because you can do this doesn't mean you should." 
     } 
    } 
    } 
} 

原理是一個範圍控制其名稱空間。這也有危險,如果你用它傻傻的(例如我用Xa,供三個不同的東西,A兩個 - 事實上,你可以用除了一個在SomeX更換標識符必須是小寫)。但是它在編寫功能代碼時也有好處 - 您不必擔心必須重命名某些迭代變量或類型,或者只是因爲您恰好將它放在不同的上下文中。

def example = { 
    val a = Array(1,2,3,4,5) 
    val sumsq = a.map(i => i*i).sum 
    a.map(i => { 
    val a = Array.range(1,i) 
    val sumsq = a.map(i => i*i).sum // Cut and paste from above, and works! 
    sumsq + i 
    }).sum 
} 

所以請注意,您有權力迷惑自己,並明智地選擇非混淆地使用該權力。

+1

謝謝,所以參考是ScalaReference第2章? – oluies 2010-09-20 16:01:35

+0

@Brent - 我不記得我在哪裏閱讀或以其他方式瞭解到這一點;我期望可以從語言規範中提取它,但我確信我以更對話的格式遇到了它。 – 2010-09-20 17:23:38

0

它不僅僅與scala有關。在大多數語言中你可以做到這一點。正如你所說的,變量在不同的範圍內。

在C#

class test 
{ 
     int i; 
     void method(int i) 
     { 
      this.i = i; 
     } 
} 

這代表了自類型。我不確定scala中的這個功能。但是你的問題的原因是範圍級別。

+3

他在詢問Type參數。具體[A]。 – 2010-09-20 13:49:07

1

那麼我相信在斯卡拉我們用同樣的規則在Java中基本上我們在Java中尋找最小的可用範圍:

class Foo<T>{ 
    T instance; 

    void <T> T getInstance(){ 
     return instance 
    } 
} 

會產生一個編譯錯誤,因爲類型T的泛型方法的getInstance聲明是不與Foo類的參數類型相同。 在斯卡拉的情況下,我相信,那麼你就寫

def checkString[A] 

你告訴編譯器,函數的行爲將根據提供的類型不同,但它與參數類外部類的沒有關係。不幸的是我現在找不到正確的地方是Scala規範。

4

我不是Scala的專家,但你的代碼行爲正是我所期望的。

首先,您需要知道方法的類型參數不需要綁定到類。

例如,以下是有效的Scala。

class Test1 { 
    def test[A] (x: A) = println(x) 
} 

而且以下也是一個有效的Scala代碼,唯一不同的是,這一個根本不使用類型A.

class Test2[A] { 
    def test (x: Int) = println(x) 
} 

所以我認爲現在很清楚,首先你創建了一個MyTest [Int]的實例,這很好。

scala> val test = new MyTest[Int] 
test: MyTest[Int] = [email protected] 

然後你叫checkString [A,INT]沒有提供類型參數,因爲它是一個通用的函數,編譯器必須推理是什麼類型A.

scala> test.checkString("String",1) 
Value is a String 
x is not a String 
res7: java.lang.String = String 

注:

在這個時間點上,Scala已經知道x必須是一個Int,並且它的類型是固定的,因爲您提供了MyTest [Int]。所以下面的代碼會產生編譯錯誤。

scala> val t = new MyTest[Int] 
t: MyTest[Int] = [email protected] 

scala> t.checkString ("A", "B") 
<console>:8: error: type mismatch; 
found : java.lang.String("B") 
required: t.MyType 
     t.checkString ("A", "B") 

現在編譯看着你提供的參數,發現其是一個對應於

checkString (value: A, x: Int) 

checkString ("String", 1) 

所以現在編譯器知道A型在checkString [A, Int]必須是一個字符串,如果你手工完成這些工作,你的代碼將如下所示。

scala> val test = new MyTest[Int] 
test: MyTest[Int] = [email protected] 

scala> test.checkString[String]("String", 1) 
Value is a String 
x is not a String 
res1: String = String 

scala> test.checkString[Int] (3, 4) 
Value is not a String 
x is not a String 
res4: Int = 3 

scala> test.checkString[Int] ("String", 4) 
<console>:8: error: type mismatch; 
found : java.lang.String("String") 
required: Int 
     test.checkString[Int] ("String", 4) 
         ^ 
+0

謝謝,我很明白,我有點驚訝的是,類和別名的類型別名都被允許成爲名稱A,而他們與彼此之間沒有任何聯繫 – oluies 2010-09-20 15:29:31

+0

Ya,因爲checkString [A]中的A被映射A MyTest [A]。就像內部塊中的相同變量名一樣。 – 2010-09-20 15:42:03

相關問題