Построение подклассов из базового абстрактного класса - PullRequest
3 голосов
/ 31 января 2011

Я хочу определить конструктор в абстрактном классе, который будет создавать конкретные подклассы.

abstract class A {
  type Impl <: A
  def construct() : Impl = {
    val res = new Impl() //compile error: class type required but A.this.Impl found
    // do more initialization with res
  }
}

class B extends A {type Impl = B}
class C extends A {type Impl = C}
//...
val b = new B
b.construct() // this should create a new instance of B

Что здесь не так? Это вообще возможно реализовать? РЕДАКТИРОВАТЬ: Уточнение: я хочу абстрагироваться от метода конструкции. Я не хочу вызывать отдельно new B и new C ни от подклассов, ни от сопутствующих объектов.

Ответы [ 6 ]

2 голосов
/ 31 января 2011

Вам нужно явно вызвать конструктор, если вы хотите создать новый экземпляр.

abstract class A {

  def newInstance(): this.type;

  def construct() : this.type = {
    val res = newInstance()
  }
}

class B extends A {
  def newInstance() = new B()
}

Scala стирает тип во время выполнения, поэтому нет способа узнать, что имел в виду Impl при создании класса.

1 голос
/ 31 января 2011

Вы можете использовать отражение, чтобы создать новый экземпляр.Нечто подобное будет работать, но, на мой взгляд, не стоит беспокоиться.Во-первых, вы сможете проверить, существует ли подходящий конструктор во время выполнения.

def newInstance[T:ClassManifest]:T = {
  val klass = implicitly[ClassManifest[T]].erasure
  val constr = klass.getConstructors()(0)
  constr.newInstance().asInstanceOf[T]
}

abstract class A {
  def construct(implicit cm:ClassManifest[this.type]): this.type = {
    val res = newInstance[this.type]
    res
  }
}

class B extends A
1 голос
/ 31 января 2011

Вы бы поместили конструктор в объект-компаньон, а не в абстрактный класс. Как это:

object A {
  def apply(i:Int):A = new B(...)
  def apply(s:String):A = new C(...)
}

Теперь вы можете создать экземпляр A, вызвав, например, A(42) или A("foobar"). Конечно, строковые и целочисленные параметры являются только примерами. Если параметры для всех конструкторов имеют одинаковые типы, эта перегрузка не будет работать. В этом случае вы можете легко создавать различные методы и вызывать их как-нибудь иначе, чем apply.

0 голосов
/ 20 декабря 2012

После моего комментария осталось Monkey ответ.Один из способов решения этой проблемы - использовать шаблон с любопытством повторяющихся шаблонов (CRTP) вместе с типами self:

abstract class A[T <: A[T]] { this: T =>

  def newInstance(): T;

  def construct(): T = {
    val res = newInstance()
    res
  }

  def some(): T = this
}

class B extends A[B] {
  def newInstance() = new B()
}

Возможно, есть лучшее решение, но покачто я нашел.

0 голосов
/ 01 февраля 2011

Я предлагаю следующую схему:

abstract class A($params) {
  // do common initialisation here
  def construct() : A

  def foo(...) = {
    ...
    val bar = construct()
    ...
  }
}

class B($moreparams) extends A($someparams) {
  // do special initialisation here
  def construct() = new B()
}

Все избыточности, которые у вас есть, составляют ровно одну строку на подкласс.Я считаю, что это небольшая цена за а) рабочее решение, которое б) не использует рефлексию (что нарушает практически все гарантии, которые предлагает вам система статического типа).

Мне все еще интересно, зачем вам это нужно construct внутри A.Запах рыбный.

0 голосов
/ 31 января 2011

Похоже, это невозможно.Согласно книге Scala (Одерски, Спун, Веннерс) нельзя создать экземпляр абстрактного типа .См. Главу «Абстрактные типы», тематическое исследование валют.Это может быть поддержано позже с «виртуальными классами».

...