Динамическая диспетчеризация Scala с параметризованной функцией

Как можно заставить этот код работать?

Насколько я знаю, в Scala нет динамической диспетчеризации (аналогично Java). Можно ли как-то смоделировать динамическую диспетчеризацию?

Или какое лучшее решение?

object Tezt {

  case class SuperClazz()
  case class SubClazz1() extends SuperClazz
  case class SubClazz2() extends SuperClazz

  def method(obj: SubClazz1) = {
    // stuff
  }

  def method(obj: SubClazz2) = {
    // stuff
  }

  def func[T <: SuperClazz](obj: T) = {
    Tezt.method(obj) // Error: Cannot resolve method reference with such signature
  }
}

person pedrorijo91    schedule 26.02.2016    source источник
comment
java тоже этого не допустит. Ваш код просто неверен. Попробуйте объяснить словами то, что вы пытаетесь сделать.   -  person Dima    schedule 27.02.2016
comment
Я сказал, что у Java тоже нет. У меня есть функция, которая получает объект, который является подклассом SuperClazz, и есть 2 метода, один для каждого подкласса. Откуда я знаю, что найти?   -  person pedrorijo91    schedule 27.02.2016
comment
Допустим, разрешили. Если передать экземпляр Subclazz3, то что?   -  person Dima    schedule 27.02.2016


Ответы (1)


Стандартный способ реализации динамической диспетчеризации для одного аргумента — это объектно-ориентированный полиморфизм:

abstract class SuperClazz() {
  def method(): ReturnType
}
case class SubClazz1() extends SuperClazz {
  def method() = {
    // stuff
  }
}
case class SubClazz2() extends SuperClazz {
  def method() = {
    // stuff
  }
}

// Alternatively just `def func(obj: SuperClazz) =` in this case
def func[T <: SuperClazz](obj: T) = 
  obj.method()

Имейте в виду, что вы не можете расширить case class другим case class, и обычно считается плохим стилем вообще расширять case classes. Чтобы реализовать это, вам, вероятно, понадобится method, чтобы быть абстрактным в SuperClazz, и, таким образом, SuperClazz должно быть trait или abstract class.

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

sealed abstract class SuperClazz()
case class SubClazz1() extends SuperClazz
case class SubClazz2() extends SuperClazz

def method(obj: SubClazz1) = {
  // stuff
}

def method(obj: SubClazz2) = {
  // stuff
}

def func(obj: SuperClazz) =
  obj match {
    case sc1: SubClazz1 => method(sc1)
    case sc2: SubClazz2 => method(sc2)
  }

Подобное сопоставление шаблонов обычно реализуется, когда суперкласс или трейт sealed (в этот случай sealed abstract class SuperClazz()). При сопоставлении объектов запечатанных суперклассов компилятор проверит, что вы указали все возможности в сопоставлении, чтобы гарантировать отсутствие ошибки времени выполнения при сопоставлении. Компилятор выдаст вам предупреждение, если вы забудете указать некоторые возможности.

Сопоставление шаблонов также работает для динамической диспетчеризации с несколькими аргументами, но по сравнению с полиморфизмом они обычно требуют написания большего количества стандартного кода и могут иметь более высокие затраты производительности во время выполнения для линейного тестирования каждого случая совпадения и вызова unapply функций.

person Kolmar    schedule 27.02.2016
comment
примечание: я не могу расширить класс case из другого класса case? Не уверен, но я не получаю никаких ошибок при этом. Кстати, почему расширение классов case считается плохой практикой? - person pedrorijo91; 27.02.2016
comment
Наследование класса case @pedrorijo91 запрещено, начиная с версии scala 2.9. Вы пытались скомпилировать код с таким наследованием? IDE может и не выдавать ошибку, но все равно не компилируется в современных версиях scala. - person Kolmar; 27.02.2016
comment
@pedrorijo91 pedrorijo91 Что касается наследования обычного класса от класса case, то это часто просто бессмысленно. Все автоматические методы класса case по-прежнему будут предоставляться родительским классом case. Например, вызов метода copy для дочернего элемента возвращает экземпляр родительского класса case. Непереопределенные equals и hashCode учитывают только те поля, которые существуют в родительском классе case. И нет автоматического объекта-компаньона с apply/unapply для дочернего элемента. Обычный способ состоит в том, чтобы иметь некоторую иерархию признаков и абстрактных классов и сделать только листья в этой иерархии наследования равными case classes. - person Kolmar; 27.02.2016