【问题标题】:How can I eliminate a parameter from constructor in a subclass in Scala如何从Scala子类中的构造函数中消除参数
【发布时间】:2020-09-24 22:00:17
【问题描述】:

我有一个名为named 的基类,如下所示。它的主要构造函数有 4 个参数,flow1flow2 的类型为 T0T1,它们派生自抽象类 Flowpredicate参数是FlowParser的对象,对于不同的EitherFlow,可以有不同的FlowParser

现在我想在子类中删除。

import scala.reflect.ClassTag
import scala.reflect._


abstract class Flow() extends Serializable {

}

trait FlowParser extends Serializable {
  def judgeIndex(path: String): Int
  def createFlow(path: String): Flow
}

class EitherFlow[T0 >: Null <: Flow: ClassTag, T1 >: Null <: Flow: ClassTag](
    predicate: FlowParser,
    var flow1: T0,
    var flow2: T1,
    var path: String) extends Flow{

  def this(predi: FlowParser, path: String) = this(predi, null, null, path)

  def this(predi: FlowParser) = this(predi, "")

  protected def this(predi: FlowParser, index: Int, d: Flow) {
    this(predi, null, null, "")
    if (index == 0) {
      this.flow1 = d.asInstanceOf[T0]
    } else {
      this.flow2 = d.asInstanceOf[T1]
    }
  }

  def this(predi: FlowParser, d: Flow) {
    this(predi, null, null, "")
    d match {
      case _: T0 => this.flow1 = d.asInstanceOf[T0]
      case _: T1 => this.flow2 = d.asInstanceOf[T1]
    }
  }
}

现在我有很多来自EitherFlow 的子类,比如EitherFlow[DataFrameFlow, RawFlow]。所以我可能会写这样的代码

new EitherFlow[DataFrameFlow, RawFlow](new MyAnnoyingFlowParser(), "")

不过new MyAnnoyingFlowParser()不是必须的,因为T0T1是确定的,MyAnnoyingFlowParser的类型是确定的,所以其实我想要这个:

new EitherDataFrameOrRawFlow("")

更新:

EitherFlow中有很多构造函数,我想做如下

// original
new EitherFlow[DataFrameFlow, RawFlow](new MyAnnoyingFlowParser(), a, b, c, ...)
// what I want
new EitherDataFrameOrRawFlow(a, b, c, ...)

我尝试了以下策略,但都遇到了一些问题。

  1. 尝试“currying”apply 方法 有了这个策略,我想我可以写这个

    val f1 = EitherDataFrameOrRawFlow("")
    val f2 = EitherDataFrameOrRawFlow(new RawFlow(), "")
    

    所以我决定写下面的代码,它实际上返回了一个匿名对象,它重载了apply方法。我从这篇文章中采用了这个策略(Scala, currying and overloading)

    object EitherFlow {
      def apply[T0 >: Null <: Flow: ClassTag, T1 >: Null <: Flow: ClassTag](predicate: FlowParser) =
        new {
          def apply[R >: Null <: Flow: ClassTag](flow: R, path: String): EitherFlow[T0, T1] = {
            val ret = new EitherFlow[T0, T1](predicate, flow)
            ret.path = path
            ret
          }
          def apply(path: String): EitherFlow[T0, T1] = {
            new EitherFlow[T0, T1](predicate, null.asInstanceOf[T0], null.asInstanceOf[T1], path)
          }
        }
    }
    

    但是,使用这种策略,我必须明确使用.apply。否则会报错

    com.xxx.flow.EitherDataFrameOrRawFlow.type does not take parameters
    
  2. 尝试创建一个新类并实现它自己的辅助构造函数

    但是,当我尝试实现辅助构造函数def this(d: Flow) 时,我感到很困惑。 根据alexander,我不能直接调用super的aux构造函数。所以我必须像下面这样写

    class EitherDataFrameOrRawFlow(var f1: DataFrameFlow, var f2: RawFlow, path: String) extends EitherFlow[DataFrameFlow, RawFlow](new DataFrameRawFlowParser(), f1, f2, path) {
      def this(d: Flow) {
        this(null, null, "")
        d match {
          case _: DataFrameFlow => this.flow1 = d.asInstanceOf[DataFrameFlow]
          case _: RawFlow => this.flow2 = d.asInstanceOf[RawFlow]
        }
      }
    }
    

    但是,我认为代码并不整洁。首先,我必须编写重复的代码,例如d match {...}。其次,我要定义重复的参数如f1/f2

【问题讨论】:

标签: scala constructor


【解决方案1】:

查看您的代码,似乎DataFlowParser 只有一个正确的实现。那么,隐含的就是合乎逻辑的路线。但是我们看不到你的设计和约束那么远。

给定约束

  • flow1 和 flow2 都是 var 的,两者都或都不能为 null,两者都有一个可用的类标签,可以充分识别擦除时的值(因此它们没有类型参数)
  • 您希望能够调用EitherDataFrameOrRawFlow(fl: Flow) 并返回EitherFlow[DataFrameFlow, RawFlow] 类型的值

我只是做

object EitherDataFrameOrRawFlow {
  def apply(fl: Flow) = new EitherFlow[DataFrameFlow, RawFlow](new MyAnnoyingFlowParser(), fl)
}

根本不创建子类。

我不清楚你传递的是新的MyAnnoyingFlowParser 还是新的DataFrameRawFlowParser,但你可以自己切换。

编辑:您也可以为对象创建一个基类:

abstract class PartiallyAppliedEitherConstructor[Flow1, Flow2](parser: => FlowParser) {
  def apply(fl: Flow) = new EitherFlow[Flow1, Flow2](parser)
  def apply(index: Int, flow: Flow) = new EitherFlow[Flow1, Flow2)(parser, index, flow)
  //etc
}

object EitherDataFrameOrRawFlow extends PartiallyAppliedEitherConstructor[DataframeFlow, RawFlow](new MyAnnoyingFlowParser())

object EitherRddOrDataFrameFlow extends PartiallyAppliedEitherConstructor[RddFlow, DataFromeFlow](new SomeOtherFlowParser())

//etc

【讨论】:

  • 嗨 Martjin,“看起来 DataFlowParser 似乎只有一个正确的实现”是不正确的。对于EitherFlow[DataFrameFlow, RawFlow]我可以有EitherDataFrameOrRawFlow,但是会有其他的EitherFlows,比如EitherFlow[DataFrameFlow, RDDFlow],所以会有多个继承FlowParser的子类
  • 我已经考虑了你的解决方案,它是实用的,但是,对于EitherFlow 的每个子类,我必须实现apply 的多个重载,它只是转发它的一些参数(除了第一个FlowParser 参数),我认为它会导致大量冗余代码。
  • @calvin 专用类型的多种应用方法不是您最初问题的一部分。您能否简明扼要地指出您的设计约束究竟是什么以及必须实现什么?
  • 您说“所以它们没有类型参数”,但是,我认为如果 T0T1 在运行时不可见,编译器是否应该警告“未选中,因为它是通过擦除消除”还是什么?但是,我没有看到这样的警告。而且我不知道如果“没有类型参数”是否有任何副作用,因为模式匹配在这里仍然有效?
  • @calvin 如果你需要检查某个值是否有某种特定的类型,类型在哪里,你需要一个TypeTag,是的
猜你喜欢
  • 1970-01-01
  • 2013-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-12
  • 1970-01-01
  • 2013-09-08
  • 1970-01-01
相关资源
最近更新 更多