为什么值不是 ArrayBuffer[Any] 中 class 的成员

Why value is not a member of class in ArrayBuffer[Any]

我刚开始学习 Scala。在 类 中,具有 valvar 的主构造函数参数是 public,而没有 valvar 的参数是私有值。所以,当我尝试执行以下代码时,一切都应该正常工作。

import scala.collection.mutable.ArrayBuffer

class Cat(val name: String)
class Dog(val name: String)

val dog = new Dog("Harry")
val cat = new Cat("Sally")

val animals = ArrayBuffer.empty[Any]
animals.append(dog)
animals.append(cat)
animals.foreach(pet => println(pet.name))

但我收到以下错误:

ScalaFiddle.scala:12: error: value name is not a member of scala.this.Any
  animals.foreach(pet => println(pet.name))  // Prints Harry Sally

为什么会这样?

ArrayBuffer.empty[Any] 指定数组缓冲区正在存储 Any 值,而不是 Cats 和 Dogs,并且 Any 不知道 [=18] =].尝试通过 Animal 特征将 CatDog 相关联

trait Animal {
  val name: String
}
class Cat(val name: String) extends Animal
class Dog(val name: String) extends Animal

然后像这样指定数组缓冲区来存储Animal

val animals = ArrayBuffer.empty[Animal]
animals.append(dog)
animals.append(cat)
animals.foreach(pet => println(pet.name))

输出

Harry
Sally

这一行:

val animals = ArrayBuffer.empty[Any]
//                              ↑↑↑

您明确告诉 Scala 您的 animals ArrayBuffer 可以包含 Any 对象。结果只能使用Anywhich are not that many.

的方法

例如,将 Int 放入 animals 中是完全合法的,因为您告诉 Scala animals 的内容可以是任何内容。但是,Int 没有 name 方法,因此如果您尝试获取 Int 的名称,您的代码将在运行时崩溃。为了防止您的代码在运行时崩溃,Scala 决定一开始就不允许您编写该代码。

因此,您需要告诉 Scala 您要将什么 种东西放入 animals。不幸的是,我们这里只有两个明确的名义类型可供我们使用:DogCat。但是,当你输入 animalsArrayBuffer[Dog] 时,你不能将 Sally 放在那里,如果你输入 ArrayBuffer[Cat],则你不能将 Harry 放在那里。

所以,我们需要输入 animals 作为允许 Dogs 和 Cats 的东西。

在 Scala 3 中,你可以使用 Union Type:

val animals = ArrayBuffer.empty[Dog | Cat]

唉,Scala 3 还差得远呢。

另一种方法是使用 Compound type with structural refinement:

val animals = ArrayBuffer.empty[{val name: String}]

这允许您的代码工作,但它可能不一定做您想要的:这允许 任何对象 有一个 val 命名为 name String 类型,而不仅仅是 DogCat 类型。特别是,您可以在 animals 中放入一些不是动物的东西,只要它有名字即可。

最好的方法可能是引入一个抽象超类型(我们称之为 Pet),它定义了一个被 CatDog 覆盖的抽象 val name , 然后键入 animals 作为 ArrayBuffer[Pet]:

trait Pet {
  val name: String
}

class Dog(val name: String) extends Pet
class Cat(val name: String) extends Pet

val animals = ArrayBuffer.empty[Pet]