我有一个抽象基类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问题,因此它必须比我似乎更简单。如何以惯用方式实现我想要的行为?
答案 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
实例的DerivedFoo
从Some
更改为None
。
这将允许您使用val
但仍然具有可变状态。
但我不会说可变状态是惯用的Scala。
您通常使用不可变val
和List
,只需要在需要更改列表时复制对象。
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
,这在您的示例中似乎就是这种情况)