Почему значение не является членом класса в ArrayBuffer [Any] - PullRequest
3 голосов
/ 09 июня 2019

Я только начал изучать скалы.В классах первичные параметры конструктора с val и var являются открытыми, тогда как параметры без val или var являются частными значениями.Итак, когда я пытаюсь выполнить следующий код, все должно работать нормально.

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

Почему это происходит?

Ответы [ 2 ]

4 голосов
/ 09 июня 2019

В этой строке:

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

Вы явно указываете Scala, что ваш animals ArrayBuffer может содержать Any объект. В результате вы можете использовать только методы Any , которых не так много .

Например, совершенно законно помещать Int в animals, поскольку вы сказали Scala, что содержимое animals может быть любым. Тем не менее, Int не имеет метода name, и поэтому ваш код взорвется во время выполнения, если вы попытаетесь получить имя Int. Чтобы предотвратить взрыв вашего кода во время выполнения, Scala скорее решает вообще не разрешать вам писать этот код.

Итак, вам нужно сообщить Scala , какие вещи вы хотите поместить в animals. К сожалению, у нас есть только два явных номинальных типа: Dog и Cat. Однако, когда вы набираете animals как ArrayBuffer[Dog], вы не можете поместить туда Салли, а если вы наберете ArrayBuffer[Cat], то вы не сможете ввести туда Гарри.

Итак, нам нужно набрать animals как нечто, допускающее как Dog s, так и Cat s.

В Scala 3 вы можете использовать Union Type :

val animals = ArrayBuffer.empty[Dog | Cat]

Увы, Scala 3 все еще довольно далеко.

Другим способом было бы использовать тип соединения с уточнением структуры :

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

Это позволяет вашему коду работать, но может не обязательно делать то, что вы хотите: это позволяет любому объекту , который имеет val с именем name типа String, а не только Dog s и Cat s. В частности, вы можете поместить в animals что-то, что не является животным, если у него есть имя.

Лучшим способом, вероятно, было бы ввести абстрактный супертип (назовем его Pet), который определяет абстрактный val name, который переопределяется на Cat и Dog, а затем введите 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]
4 голосов
/ 09 июня 2019

ArrayBuffer.empty[Any] указывает, что буфер массива хранит значения Any, а не Cat с и Dog с, а Any не знает о name.Попробуйте связать Cat и Dog через Animal черту, например,

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
...