2010-09-20 81 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

相关问题