声明带参数化类型参数的方法vs将参数声明为Any的优点?

时间:2015-04-21 02:06:41

标签: scala functional-programming

我最近遇到过这个例子,打印出“[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
}

我很想知道第一种方法的优点是什么?

1 个答案:

答案 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,但现在由我们来记住类型,如果我们记错了,则转换会在运行时失败,导致我们的程序崩溃。使用泛型,编译器可以为我们跟踪正确的类型。