2016-10-17 8 views
0

У меня есть абстрактный базовый класс, Foo, конструктор которого я бы хотел иметь необязательный параметр. Если ни один не предоставлен, я просто дам ему значение None.Как я могу расширить абстрактный класс с необязательным членом в Scala?

Источник 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, поэтому она должна быть проще, чем я, кажется, это сделать. Как я могу достичь желаемогоповедение идиоматично?

ответ

1

мне удалось это исправить после прочтения documentation:

Параметры конструктора корпусных классов рассматриваются как общественные ценности и могут быть доступны непосредственно.

Поскольку мой базовый класс является абстрактным, я могу просто расширить его по умолчанию, val.

Мне просто нужно указать, что parentIds является var в конструкторе DerivedFoo.

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) { } 
+0

Yep хорошо, только что увидел, что ты прав –

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) { } 
+0

Обратите внимание, что тогда 'DerivedFoo' будет иметь _both_ членов, поэтому убедитесь, что вы получили доступ к правильному. Может быть, вы хотите дать им более описательные имена, чтобы обозначить это: 'parentIds' для абстрактного конструктора классов и что-то вроде' prepParentIds' для конструктора DerivedFoo –

+0

Это единственное решение? :( – erip

+0

Не вешайте, я дам вам еще один из немногих –

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 и обратиться к членам, как вы, вероятно, ожидали, и вы не будете иметь двух членов с разными именами.

РЕПЛ выход:

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)) 
0

Для мутации, вы можете import scala.collection.mutable и использовать mutable.ListBuffer вместо List. Я предполагаю, конечно, что вы не будете изменять parentIds примера DerivedFoo от Some до None. Это позволит вам использовать val с, но все еще имеет изменяемое состояние.

Но я бы не сказал, что изменчивое состояние - это идиоматическая Скала.

Вы обычно используете неизменный 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, только если у вас есть все подклассы, которые, как представляется, в вашем примере)

 Смежные вопросы

  • Нет связанных вопросов^_^