如何在Scala中使用可选成员扩展抽象类?

时间:2016-10-17 16:12:41

标签: scala oop abstract-class case-class

我有一个抽象基类Foo,我的构造函数我想要一个可选参数。如果没有提供,我只会给它一个None值。

源Foo不会有父母,所以我只想在没有父母列表的情况下构建它们(保留父列表的默认值)

派生的Foo可能提供了父母,所以我想模仿Foo基类的签名。

以下是我的尝试:

abstract class Foo(val id: String, var parentIds: Option[List[String]]=None) { }

case class SourceFoo(override val id: String)
  extends Foo(id, parentIds=None) { }

case class DerivedFoo(override val id: String, 
                      override var parentIds: Option[List[String]])
  extends Foo(id, parentIds) { }

我遇到编译错误,无法覆盖可变变量(引用parentIds构造函数中的DerivedFoo

此列表可能会更改,因此我不想将其设为val(这会消除我的编译器问题)。

这是一个非常基本的OO问题,因此它必须比我似乎更简单。如何以惯用方式实现我想要的行为?

4 个答案:

答案 0 :(得分:1)

我在阅读documentation

后设法解决了这个问题
  

案例类的构造函数参数被视为公共值,可以直接访问。

由于我的基类是抽象的,我可以使用默认的val构造来扩展它。

我只需要指定parentIds是DerivedFoo构造函数中的var。

abstract class Foo(id: String, parentIds: Option[List[String]]=None) { }

case class SourceFoo(id: String) extends Foo(id) { }

case class DerivedFoo(id: String, var parentIds: Option[List[String]]=None) 
    extends Foo(id, parentIds) { }

答案 1 :(得分:1)

这是另一种可能更好的方法。明确承认类参数和类成员之间的区别。如果您喜欢这段代码,也可以将它们设为私有成员。

abstract class Foo(identifier: String, parentIdentifiers: Option[List[String]]) { 
  val id = identifier
  var parentIds = parentIdentifiers
}

case class SourceFoo(override val id: String) extends Foo(id, parentIdentifiers = None) { }

case class DerivedFoo(identifier: String, parentIdentifiers: Option[List[String]]) extends Foo(identifier, parentIdentifiers) { }

之后,您可以创建DerivedFoo并按照您可能期望的那样引用成员,并且您不会有两个具有不同名称的成员。

REPL输出:

scala> DerivedFoo("1", Some(List("200","201","202")))
res0: DerivedFoo = DerivedFoo(1,Some(List(200, 201, 202)))

scala> res0.parentIds
res1: Option[List[String]] = Some(List(200, 201, 202))

scala> res0.parentIds = Some(List("800", "801", "802"))
res0.parentIds: Option[List[String]] = Some(List(800, 801, 802))

答案 2 :(得分:0)

我认为您可以通过更改抽象类中参数的名称来实现目标。

abstract class Foo(val id: String, var parentIdentifiers: Option[List[String]]) { 
  parentIdentifiers = None
}

case class SourceFoo(override val id: String)
  extends Foo(id, parentIdentifiers = None) { }

case class DerivedFoo(override val id: String, 
                      var parentIds: Option[List[String]])
  extends Foo(id, parentIds) { }

答案 3 :(得分:0)

对于变异,您可以import scala.collection.mutable并使用mutable.ListBuffer代替List。 我当然假设您不会将parentIds实例的DerivedFooSome更改为None。 这将允许您使用val但仍然具有可变状态。

但我不会说可变状态是惯用的Scala。

您通常使用不可变valList,只需要在需要更改列表时复制对象。

  val fooA = SourceFoo("a")
  val fooB = DerivedFoo("b", "a" :: Nil)
  val fooB2 = fooB.copy(parentIds = fooB.parentIds :+ "x")

因此,为了更加惯用,你能做的最简单就是

sealed abstract class Foo(val id: String, val parentIdsOpt: Option[List[String]])

case class SourceFoo(override val id: String)
  extends Foo(id, None)

case class DerivedFoo(override val id: String, val parentIds: List[String])
  extends Foo(id, Some(parentIds))

这与你所拥有的非常接近。

请注意,DerivedFoo.parentIds不再是Option,因为DerivedFoo始终有父母,因此您不必处理Option。 (你仍然需要处理空列表)

还要注意特征上的sealed关键字,这不是必需的,但如果要匹配抽象类或特征的实例,则建议使用该关键字。 (只有拥有所有子类时才可以使用sealed,这在您的示例中似乎就是这种情况)