派生类作为类构造函数参数 [英] Derived classes as class constructor parameters

查看:102
本文介绍了派生类作为类构造函数参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我现在有一个类层次结构,大致如下:

I have a class hierarchy that is, right now, roughly as follows:

sealed trait Foo {
  def method: Any
}

case class Bar extends Foo {
  def method: Array[String] = // implementation
}

case class Baz extends Foo {
  def method: Map[String, Array[String]] = // implementation
}

我给抽象方法的返回类型为Any,因为案例类的返回类型必然不同,但是它们具有相似的目的.出于这个原因,我想将其保留在特征中,以对这种常见行为进行建模,这是我发现对其进行编译的唯一方法.我意识到这与Scala的类型系统的精神背道而驰,所以我问以下第一个问题.

I am giving the abstract method a return type of Any because the return types of the case classes are necessarily different, but they share a similar purpose. For this reason, I'd like to keep it in the trait to model that common behavior, and this is the only way I've found to make it compile. I realize this is against the spirit of Scala's type system, so I ask the first question below.

然后,另一个类期望Foo的子类作为构造函数参数,除以下内容外,我不知道如何表示这一点:

Then, another class expects a subclass of Foo as a constructor parameter, and I do not know how to denote this, other than the following:

class Qux(foo: Foo) {
  val m = foo.method
  ...
  ...
}

稍后在类Qux中有些方法期望val m的类型与Foo的特定子类(BarBaz)相对应,但是我遇到编译错误像

Later in the class Qux there are methods that expect the val m to be of the type corresponding to the particular subclass (Bar or Baz) of Foo, but I am getting compilation errors like

... type mismatch;
[error]  found   : Any
[error]  required: Array[String]

所以我有几个问题:

  • 我对Scala足够熟悉,以为这是代表我的特定问题的正确方法,但是对它却不熟悉,却不知道如何解决.做我想做的事情的正确方法是什么?
  • 此外,还有一种方法可以告诉类Qux应该将m视为由BarBaz的特定method返回的值,而不是作为Foo的抽象方法返回的值?
  • I am familiar enough with Scala to believe that this is the right way to represent my particular problem, but not familiar enough with it to know how to go about it. What is the proper way to do what I'm trying to do?
  • Also, is there a way to tell class Qux that m should be treated as the value returned by the specific method from Bar or Baz, and not the abstract method from Foo?

采用@marios建议的方法(使用抽象类型成员)似乎是朝正确方向迈出的一步,但是现在出现了类型不匹配的情况.在课程Qux中,我现在有了

Taking the approach suggested by @marios (using abstract type members) seems to be a step in the right direction, but a type mismatch pops up now. Within the class Qux, I now have

class Qux[X <: Foo](sc: SparkContext, val foo: X) {
  val m: foo.A = foo.method

  def process(rows: DataFrame) = foo match {
    case Bar(sc, _) => BarProcessor(sc, m).doStuff(rows)
    case Baz(sc, _) => BazProcessor(sc, m.keys).doStuff(rows, m.values)
  }
}

例如,用Array[String]实例化BarProcessor的地方,BazProcessor需要Bazmethod返回的值中的键值对来完成工作.但是,我现在遇到类似

Where BarProcessor is instantiated with, for instance, an Array[String], and BazProcessor needs the key-value pairs from the value returned by Baz's method to do stuff. However, I am now getting errors like

[error] Qux.scala:4: type mismatch;
[error]  found   : Qux.this.foo.A
[error]  required: Array[String]
[error]     case Bar(sc, _) => BarProcessor(sc, m).doStuff(rows)
[error]                                         ^

fooBaz时(沿着value keys is not a member of Qux.this.foo.A等),当我尝试在m上调用特定于Map的方法时,出现类似的错误.我知道m并不是真的Array[String],而是A类型.但是,有没有一种方法可以告诉Scala将其翻译"成所需的类型呢?

Similar errors show up when I try to call Map-specific methods on m when foo is a Baz (along the lines of value keys is not a member of Qux.this.foo.A, etc.). I understand that m isn't really an Array[String] -- it's of type A. But is there a way to tell Scala to "translate" this into its desired type?

推荐答案

访问ADT中单个类型的更简单方法是使用抽象类型成员,而不是通用类型参数.

An easier way to access the individual types in your ADT is to use an abstract type member instead of a generic type parameter.

sealed trait Foo {
   type A
   def method: A
}

case object Bar extends Foo {
   type A = Array[String]
   def method: A = Array.empty[String]
}

case object Baz extends Foo {
   type A = Map[String, Array[String]]
   def method: A = Map.empty[String, Array[String]]
}

case class Qux[X <: Foo](foo: X) {
  def m: X#A = foo.method

  // You can then pattern match on m
  def f = m match {
    case a: Baz.A => a.size // Use Baz#A if Baz is a class and not an object
    case b: Bar.A => b.size // Use Bar#A if Bar is a class and not an object
  }
}

使用它(查看返回类型)

Using it (look at the return types)

@ Qux(Baz).m
res6: Map[String, Array[String]] = Map()

@ Qux(Bar).m
res7: Array[String] = Array()

这篇关于派生类作为类构造函数参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆