我想知道为什么根据下面的示例允许具有相同名称(“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
答案 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."
}
}
}
}
原则是范围可以控制其命名空间。这有危险,如果你愚蠢地使用它(例如我已经为三个不同的东西使用了X
和a
,并且两个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中的这个功能。但你问题的原因是范围水平。