从基本抽象类构造子类

发布于 2024-10-15 04:53:04 字数 516 浏览 8 评论 0原文

我想在抽象类中定义一个构造函数来创建具体的子类。

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 Bnew C

I want to define a constructor in an abstract class that will create concrete subclasses.

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

What is wrong here? Is this even possible to implement?
EDIT: Clarification: I want to abstract over the construct method. I do not want to call separately new B and new C from either subclasses or companion objects.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

终难愈 2024-10-22 04:53:04

如果要创建新实例,则需要显式调用构造函数。

abstract class A {

  def newInstance(): this.type;

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

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

Scala 在运行时擦除类型,因此无法知道创建类时 Impl 的含义。

You need to explicitly invoke a constructor if you want create a new instance.

abstract class A {

  def newInstance(): this.type;

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

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

Scala erases type at runtime so there is no way to know what Impl meant when the class was created.

慢慢从新开始 2024-10-22 04:53:04

您可以将构造函数放在伴生对象中,而不是放在抽象类中。像这样:

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

现在,您可以通过调用 A(42)A("foobar") 创建 A 的实例,例如。当然,字符串和整数参数只是示例。如果所有构造函数的参数具有相同的类型,则此重载将不起作用。在这种情况下,您可以轻松创建不同的方法并将它们称为 apply 以外的名称。

You would put the constructor in the companion object, not in the abstract class. Like this:

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

Now, you could create an instance of A by calling A(42), or A("foobar"), for example. The string and integer parameters are only examples, of course. If the parameters for all the constructors have the same types, this overloading will not work. In that case, you can easily create different methods and call them something other than apply.

鲜血染红嫁衣 2024-10-22 04:53:04

您可以使用反射来创建新实例。像这样的事情会起作用,但在我看来不值得这么麻烦。一方面,您只能检查运行时是否存在合适的构造函数。

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

You can use reflection to create a new instance. Something like this would work but in my opinion is not worth the trouble. For one thing you would only be able to check if a suitable constructor existed at runtime.

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
梦幻的味道 2024-10-22 04:53:04

看来这是不可能的。根据 Scala 书(作者:Oderski、Spoon、Venners),您无法创建抽象类型的实例。请参阅:抽象类型章节,货币案例研究。稍后可能会通过“虚拟类”来支持这一点。

Looks like this is not possible. According to the Scala book (by Oderski, Spoon, Venners) you cannot create an instance of an abstract type. See: Abstract Types chapter, Currencies case study. This may be supported later with "virtual classes".

帅气尐潴 2024-10-22 04:53:04

我建议采用以下模式:

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()
}

您现在拥有的所有冗余恰好是每个子类一行。我认为这是一个很小的代价:a)一个可行的解决方案,b)不使用反射(这基本上破坏了静态类型系统为您提供的所有保证)。

我仍然很好奇为什么你需要在 A 中构建construct。闻起来有鱼腥味。

I propose the following pattern:

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()
}

All reduandancy you have now is precisely one line per subclass. I consider this a small price to pay for a) a working solution that b) does not use reflection (which breaks essentially all guarantees a static type system offers you).

I am still curious why you would need construct inside A. Smells fishy.

瑕疵 2024-10-22 04:53:04

按照我在 Monkey 回复中留下的评论。解决此问题的一种方法是使用 Curiously Recurring Template Pattern (CRTP) 和self types:

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()
}

也许有更好的解决方案,但这是迄今为止我发现的。

Following my comment left at Monkey response. One way how to solve this is to use the Curiously Recurring Template Pattern (CRTP) together with a self types:

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()
}

Perhaps there is a better solution, but this is so far what I found.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文