简体   繁体   English

超类型中的Scala复制方法

[英]Scala copy method in supertype

I have an abstract class (this is an analogous example) Printer which prints the supplied argument unless it fails to pass a filter. 我有一个抽象类(这是一个类似的示例) Printer ,它打印提供的参数,除非它没有通过过滤器。 Printer is contravariant wrt its generic type T . Printer与其通用类型T是相反的。

class Printer[-T] {
  val filters: Seq[Function2[T => Boolean]]

  def print(t: T): Unit {
    if (filters.forall(_(t))) doPrint(t)
  }
  def doPrint(t: T): Unit
}

I now have 20 subclasses of Printer -- one for Strings, Ints, etc. Since Printer is contravariant, filters must be a val . 现在,我有20个Printer子类-一个用于String,Ints等。由于Printer是互变的,所以filters必须是val However if I want a method of Printer to add a filter it needs to be immutable. 但是,如果我想让Printer的方法添加过滤器,则它必须是不可变的。

def addFilter[TT <: T](t: TT): Printer[TT]

Unfortunately I now need to implement this method in each of my 20 subclasses. 不幸的是,我现在需要在我的20个子类中的每一个中实现此方法。 Is there any way around this? 有没有办法解决?

Update: Also, in addFilter , I don't know how I can return the subclass instead of the superclass Printer . 更新:另外,在addFilter ,我不知道如何返回子类而不是超类Printer For example, if I called addFilter on a StringPrinter I would ideally get the type StringPrinter back. 例如,如果我在StringPrinter上调用addFilter ,则理想情况下将返回StringPrinter类型。

THe following is a bit of a departure from the way you have coded your Printer , but potentially could fulfil your intentions. 以下内容与您对Printer进行编码的方式有些偏离,但可能会实现您的意图。 Note that this way of coding your class enables a greater separation of concerns: you can define filters and print implementations (the doPrint argument) independently of how they are being used: 请注意,这种对类进行编码的方式可以实现更大的关注点分离:您可以独立于使用方式来定义过滤器和打印实现( doPrint参数):

case class Printer[T](val filters: List[Function1[T, Boolean]], val doPrint: T => Unit) {
  def print(t: T): Unit = {
    if (filters.forall(_(t))) doPrint(t)
  }

  def addFilter[TT <: T](f: TT => Boolean): Printer[TT] = copy(f :: filters)
}

Note that I haven't needed to specify contravariancy here, not sure if that will be an issue for you. 请注意,我并不需要在这里指定矛盾性,不确定是否会给您带来麻烦。

To use the class, you don't need to subclass, just pass suitable arguments into the constructor (actually, the companion apply factory method provided for free for case classes) - eg: 要使用该类,您不需要子类化,只需将适当的参数传递给构造函数(实际上,同伴为case类免费提供了apply工厂方法)-例如:

case class Foo(x: Int)
val fooChk1: Foo => Boolean = (f: Foo) => f.x != 1
val fooPrinter1 = Printer(fooChk1 :: Nil, (f: Foo) => println(s"Foo: x = ${f.x}"))

val fooChk3: Foo => Boolean = (f: Foo) => f.x != 3
val fooPrinter2 = fooPrinter1.addFilter(fooChk3)

val foo3 = Foo(3)
fooPrinter1.print(foo3) // Prints 'Foo: x = 3'
fooPrinter3.print(foo3) // Prints nothing

There is also a fair bit of scope for using implicits here, too - both for parameters to Print (eg. change the constructor to (val filters: List[Function1[T, Boolean]])(implicit val doPrint: T => Unit) ), and for specific Print[X] variants. 在这里使用隐式也有相当大的范围-都可以Print参数(例如,将构造函数更改为(val filters: List[Function1[T, Boolean]])(implicit val doPrint: T => Unit) ),以及特定的Print[X]变体。

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

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