我试图理解scala中的类型参数概念。
$ source ~/.profile
$ rake db:create
$ rake db:migrate
下面:
def sum [A] (a:A):A={a}
// used single parameter and its working fine, able to pass any data type.
上面的函数有一个类型参数,我没有提到任何数据类型 - 那么它如何被视为字符串?
答案 0 :(得分:4)
def sum[A](a:A,b:A):A={a+b}
您要添加T
+
T
,编译器无法推断+
T
,它将使用默认implicit +
}:any2stringadd
,因此编译器会抛出required: String
错误。
请参阅https://issues.scala-lang.org/browse/SI-8229
implicit final class any2stringadd[A](private val self: A) extends AnyVal {
def +(other: String): String = String.valueOf(self) + other
}
答案 1 :(得分:3)
让我们从一个不同的角度来看待这个:实际上,是一个参数是什么?这是什么意思?
让我们从你可能更熟悉的东西开始:值参数。子程序中的值参数是什么意思?说,我们有这样的事情:
def foo(a, b) = { /* … */ } // deliberately ignoring types and the body
这意味着:我有一个包含两个参数foo
和a
的子例程b
。子例程是 general ,它不关心a
和b
的实际具体值是什么。它适用于a
和b
的所有值,因为它甚至知道 a
和{{的值1}}是。
更具体一点:
b
同样,def plus(a: Int, b: Int) = a + b
不知道plus
和a
的实际值是什么。它适用于b
和2
以及3
和23
或42
和0
。它完全无视0
和a
的具体价值观。它只知道b
和a
存在。
现在,类型参数是相同的,只是在类型级别而不是值级别。
b
在类型级别意味着同样的事情,blabla[A]
意味着在价值层面:“我有一个东西,我不知道它是什么(我不在乎),但我会打电话它blabla(a)
。“
所以,你有效地做了什么,就是告诉Scala你对A
一无所知。但是你用它做了一些事情(或者更确切地说是它的实例):你添加它。但是,你怎么知道你可以添加它?你甚至都不知道它是什么!
所以,添加无法可能正常工作,必须失败!
然而,它失败的方式有点奇怪,并且与scala.Predef
对象中的一些预定义的隐式转换有关。特别是there is an implicit conversion for string concatenation, which can convert an arbitrary object into a String
and then concatenate another String
to it。在这种情况下,它会将A
转换为any2stringadd
,然后尝试向其添加a
,但any2stringadd.+
方法仅将b
作为参数,因此您会得到一个奇怪的错误消息,即它期待String
。
当您遇到复杂的类型错误时,有时会弹出其他类似的类型:
Any
,AnyRef
,AnyVal
:这些类型位于Scala类型层次结构的最顶层。有时候,如果你有一些编程错误,你认为你从两个不同的代码路径返回相同的类型,但实际上你返回两种不同的类型,Scala仍然会尝试推断两者之间的共同类型,并最终得到只有共同的祖先是String
,Any
或AnyRef
。 (这有点像Java或C♯中的AnyVal
。)Serializable
:这实际上和上面一样。许多完全不同的类型实现了Object
接口,因此有时当你有两种非常不同的类型,你实际上期望相同的类型时,Scala会帮助推断Serializable
作为最接近的共同祖先,然后大喊大叫你做了Serializable
它不支持的东西。Product
是所有Serializable
的超级特征(即Product1
,Product2
,... Product22
),因此所有Product
s(即Tuple1
,Tuple2
,... Tuple22
)。 Tuple
也混合在所有Product
es中。所以,如果你有两个case class
个不同的arity或两个不相关的Tuple
es(例如你有时会返回None
但是然后意外地返回case class
而不是Some(somethingOfTypeFoo)
),那么Option
和somethingOfTypeFoo
之间最精确的常见类型将被推断为case class Foo
或... Product
:也可以收到上述内容的组合。例如。 Product with Serializable
是Tuple
,因此此实际上是两个Serializable
不同arity中最精确的常见类型。遇到这些问题的一种常见方法是使用不带Tuple
的条件表达式:
else
这是什么类型的回归? Int
?没有! if (true) 42
分支返回then
,但Int
分支返回 nothing (因为没有else
分支)。 Scala实际上有一个不返回任何内容的返回类型:Unit
。 else
是Unit
的子类型,AnyVal
是Int
的子类型,因此条件表达式的两个分支的类型最接近的共同祖先实际上是{{ 1}},而不是AnyVal
。 (注意:AnyVal
分支无法访问的事实与打字角度无关。可达性是一个运行时事物,类型是编译时间。)