简体   繁体   English

Scala:父类有没有办法访问仅由子节点定义的方法?

[英]Scala: Is there a way for a parent class to access methods defined only by children?

I have two case classes that inherit from an abstract base class. 我有两个从抽象基类继承的case类。 I want to define some methods on the abstract base class that use the copy methods on the inheriting case classes (and so return an instance of the child class.) Is there a way to do this using self types? 我想在抽象基类上定义一些方法,这些方法在继承case类上使用复制方法(因此返回子类的实例。)有没有办法使用self类型?

Example code: 示例代码:

abstract class BaseClass(a: String, b: Int) {
  this: case class => //not legal, but I'm looking for something similar

  def doubleB(newB: Int) = this.copy(b = b * 2) //doesn't work because BaseClass has no copy
}

case class HasC(a: String, b: Int, c: Boolean) extends BaseClass(a, b) {
  def doesStuffWithC(newC: Boolean) = {
    ...
  }
}

case class HasD(a: String, b: Int, D: Double) extends BaseClass(a, b) {
  def doesStuffWithD(newD: Double) = {
    ...
  }
}

I've figured out how to get the result I want thanks to this question: How to use Scala's this typing, abstract types, etc. to implement a Self type? 由于这个问题,我已经想出如何得到我想要的结果: 如何使用Scala的这种类型,抽象类型等来实现Self类型? but it involves adding a makeCopy method to BaseClass and overriding it with a call to copy in each of the child case classes, and the syntax (especially for the Self type) is fairly confusing. 但它涉及向BaseClass添加一个makeCopy方法,并通过在每个子案例类中调用复制来覆盖它,并且语法(特别是对于Self类型)相当混乱。 Is there a way to do this with Scala's built in self typing? 有没有办法用Scala的内置自键型来做到这一点?

You can't do what you want because copy needs to know about all the possible parameters . 你无法做你想做的事,因为copy 需要知道所有可能的参数 So even if case classes inherited from Copyable , it wouldn't be the copy you needed. 因此,即使从Copyable继承的case类,它也不是你需要的copy Also, if you're going to keep the types straight, you'll be thwarted by Scala's lack of a " MyType ". 此外,如果你要保持这些类型,你会被Scala缺乏“ MyType ”所阻碍。 So you can't just extend a base class. 所以你不能扩展一个基类。 However, you could add an abstract method and type annotation: 但是,您可以添加抽象方法并键入注释:

abstract class BaseClass[C <: BaseClass[_]](a: String, b: Int) {
  def setB(b0: Int): C
  def doubleB(b0: Int) = setB(b0*2)
}
case class HasC(a: String, b: Int, c: Boolean) extends BaseClass[HasC](a,b) {
  def setB(b0: Int) = this.copy(b = b0)
  def doesStuffWithC(c0: Boolean) = doubleB(if (c0) b else -b).copy(c = c0)
}

And then you can: 然后你可以:

scala> HasC("fish",1,false).doesStuffWithC(true)
res47: HasC = HasC(fish,2,true)

This extra work will be worth it if you have a lot of shared functionality that depends on the ability to copy just b (either many methods, or a small number of complicated methods)--that is, this solves the DRY issue. 如果你有很多共享功能取决于复制b的能力(很多方法,或者很少的复杂方法),这个额外的工作将是值得的 - 也就是说,这解决了DRY问题。 If instead you want to abstract over HasC and other derived classes, you can either use BaseClass[_] or add yet another level that defines setB(b0: Int): BaseBase or simply forget the type parameterization and use BaseClass as the return type (but recognize that HasC cannot use BaseClass methods and still retain its type identity). 如果您想要抽象HasC和其他派生类,您可以使用BaseClass[_]或添加另一个定义setB(b0: Int): BaseBase级别setB(b0: Int): BaseBase或者只是忘记类型参数化并使用BaseClass作为返回类型(但是要认识到HasC不能使用BaseClass方法并且仍然保留其类型标识)。

I think you're out of luck. 我觉得你运气不好。 The copy methods on HasC and HasD have different signatures. HasCHasD上的copy方法具有不同的签名。 It's a bit hidden because of the default arguments, but basically the definition in BaseClass wouldn't know which copy method to call. 由于默认参数,它有点隐藏,但基本上BaseClass的定义不知道要调用哪个copy方法。

You could define a makeCopy in the abstract class that takes a copier function that takes Unit and returns a BaseClass, then, in your methods that use it (like doubleB) override them in the case class bodies and make use of makeCopy by passing it an anonymous function that does the work of creating a new copy with the props changed, like so: 你可以在抽象类中定义一个makeCopy,它接受一个复制器函数,它接受Unit并返回一个BaseClass,然后,在你使用它的方法(如doubleB)中覆盖它们在case类体中,并通过传递它来使用makeCopy匿名函数,用于更改道具创建新副本的工作,如下所示:

package delegatedcopy

abstract class BaseClass(a: String, b:Int){
  def aField = a
  def bField = b
  def doubleB:BaseClass
  def makeCopy(copier: () => BaseClass):BaseClass = copier()
}

case class HasC(override val aField: String, override val bField: Int, cField: Boolean) extends BaseClass(aField, bField){
  override def doubleB:BaseClass = makeCopy( ()=> HasC(aField, bField * 2, cField) )
}

case class HasD(override val aField: String, override val bField: Int, dField:Double) extends BaseClass(aField, bField){
  override def doubleB:BaseClass = makeCopy( ()=> HasD(aField, bField * 2, dField) )
}

A test app that demonstrates it: 一个演示它的测试应用程序:

import delegatedcopy._

object TestApp extends Application{
  val hasC = HasC( "A C object", 5, true)
  val hasD = HasD( "A D object", 2, 3.55)
  val hasCDoubleB = hasC.doubleB
  val hasDDoubleB = hasD.doubleB

  println(hasC) // prints HasC(A C object,5,true)
  println(hasCDoubleB) //prints HasC(A C object,10,true)

  println( hasD ) // prints HasD(A D object,2,3.55)
  println( hasDDoubleB ) // prints HasD(A D object,4,3.55)
}

In this way, you are able to keep the makeCopy method the same for all children classes as in the base class, and can probably implement or mix in quite a bit of functionality in the base and case classes while keeping common code in a safe place and being able to pass clients a BaseClass and pattern match on the specific case classes. 通过这种方式,您可以使所有子类的makeCopy方法与基类保持一致,并且可以在基类和案例类中实现或混合相当多的功能,同时将公共代码保存在安全的位置并能够在特定案例类中传递客户端BaseClass和模式匹配。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM