两个具有相同名称的类型参数

时间:2010-09-20 13:18:50

标签: scala generics

我想知道为什么根据下面的示例允许具有相同名称(“A”)的两个类型参数(名为“A”)。我知道这是类型参数的不良命名,不要这样做。

(我的猜测是它们处于不同的范围级别,例如类级别和功能级别,并且编译器正在使用某种名称修改)

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] = MyTest@308ff65f

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

4 个答案:

答案 0 :(得分: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 - 事实上,你可以替换 X的每个标识符除外Some必须为小写的标识符除外)。但是在编写功能代码时它也有好处 - 你不必担心必须重命名一些迭代变量或类型,或者只是因为你碰巧把它放在不同的上下文中。

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 :(得分: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] = MyTest@308ff65f

然后你调用了checkString [A,Int]而没有提供类型参数A,因为它是一个泛型函数,编译器必须推断出什么类型是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] = MyTest@cb800f

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

现在,编译器会查看您提供的参数,并发现它是

checkString ("String", 1)

对应

checkString (value: A, x: Int)

所以现在编译器知道checkString [A,Int]中的类型A必须是一个字符串,如果你手动完成所有这些,你的代码将如下所示。

scala> val test = new MyTest[Int]
test: MyTest[Int] = MyTest@5bda13

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)
                          ^    

答案 2 :(得分:1)

我相信scala我们使用与Java相同的规则基本上我们正在寻找Java中最小的可用范围:

class Foo<T>{
   T instance;

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

将产生编译错误,因为在泛型方法中声明的类型T getInstance与类Foo的参数类型不同。 在Scala的情况下,我相信你写了

def checkString[A]

告诉编译器函数行为会因提供的类型而异,但它与外部类的参数类没有关联。不幸的是,我现在找不到正确的地方Scala spec。

答案 3 :(得分:0)

它不仅与scala有关。在大多数语言中,您可以这样做。因为正如你所说,变量的范围不同。

在c#

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

这代表了自我类型。我不确定scala中的这个功能。但你问题的原因是范围水平。