我最近遇到过这个例子,打印出“[7]”
class Decorator(left: String, right: String) {
def layout[A](x: A) = left + x.toString() + right
}
def apply(f: Int => String, v: Int) = f(v)
val decorator = new Decorator("[", "]")
println(apply(decorator.layout, 7))
还可以像这样声明Decorator
:
class Decorator(left: String, right: String) {
def layout(x: Any) = left + x.toString() + right
}
我很想知道第一种方法的优点是什么?
答案 0 :(得分:2)
在您给出的具体示例中没有太大区别,因为您对该值执行的唯一操作就是调用toString
上的方法,该方法在Any
上定义,因此可用于Scala中的任何值。
但是,通常情况下,您可能希望保存该数据并在以后使用它。假设我们有一个名为Cat
的自定义类型:
case class Cat(name: String) {
def speak() = print(s"Meow, my name is $name.\n")
}
我们可能想要存储猫的列表,并且仍然能够将猫从列表中取出并将它们视为猫。 List[A]
类允许我们这样做。请注意,它具有类型参数A
,这将允许它记住它所保存的事物的类型,并在以后使用正确的类型返回它们。例如,我们可以编写一个函数来让我们列表中的所有猫说话。
def processCats(cats: List[Cat]) =
cats.foreach((cat:Cat) => cat.speak)
这是可能的,因为cat
中的变量foreach
的类型为Cat
,因此我们可以调用Cat
的方法。
如果List
不是通用的,那么变量cat将无法具有类型Cat
,并且如果没有运行时强制转换,我们将无法使用它。 List
实际上并不像这样工作,但我们可以模拟它:
type NonGenericList = List[Any]
def nonGenericProcessCats(cats: NonGenericList) =
cats.foreach((catAsAny:Any) => {
val cat = catAsAny.asInstanceOf[Cat]
cat.speak
})
这次,catAsAny
中的变量foreach
的类型为Any
。我们可以使用Cat
将其转换为asInstanceOf
,但现在由我们来记住类型,如果我们记错了,则转换会在运行时失败,导致我们的程序崩溃。使用泛型,编译器可以为我们跟踪正确的类型。