質問

I have the following class hierarchy.

sealed trait Foo {
  val a: String 
}
case class Bar1(a: String) extends Foo
case class Bar2(a: String) extends Foo 

Now I want to add a convenient method to modify the field a. I need this method to be accessible in the super type Foo and I want to use the .copy method of the case class (because I actually have a lot more fields and it is painful to use the constructor). My first attempt was to use pattern matching :

sealed trait Foo {
  val a: String
  def withField(b: String) = this match {
    case b1: Bar1 => b1.copy(a = b)
    case b2: Bar2 => b2.copy(a = b)
  }
}

Now I would also like my withField method to return an instance type of the caller, B1 if the method is called by an instance of type B1, B2 if the method is called by an instance of type B2 and Foo if this is all I know. So I thought to myself I might be able to parametrized the method withFieldto serve this purpose. Something like:

sealed trait Foo {
  val a: String
  def withField[A <: Foo](b: String) = this match {
    case b1: Bar1 => b1.copy(a = b)
    case b2: Bar2 => b2.copy(a = b)
  }
}

but I don't manage to parametried withField with the type of this.

Am I getting completely wrong here ? Should I use a different pattern maybe using override modifier ?

Thanks a lot

役に立ちましたか?

解決

Am I getting completely wrong here ? Should I use a different pattern maybe using override modifier ?

Yes. There are two alternatives:

sealed trait Foo {
  val a: String
  def withField(b: String): Foo
}

case class Bar1(a: String) extends Foo {
  // return types are covariant, and Bar1 is subtype of Foo,
  // so this is legal
  def withField(b: String): Bar1 = ...
}

or

sealed trait Foo[ThisType <: Foo[ThisType]] {
  val a: String
  def withField(b: String): ThisType
}

case class Bar1(a: String) extends Foo[Bar1] {
  def withField(b: String): Bar1 = ...
}

Note the second is more complex, and should only be used if you actually need it.

EDIT to answer Christian's question:

sealed trait Foo {
  type ThisType <: Foo
  def withField(b: String): ThisType = (this match {
    case b1: Bar1 => b1.copy(a = b)
    ...
  }).asInstanceOf[ThisType]
}

case class Bar1(a: String) extends Foo {
  type ThisType = Bar1
}

I don't like it: it needs a cast, actually using it would require dependent method types, and I wouldn't be surprised if it broke down in practice (e.g. because the compiler can't prove foo.ThisType and foo.withField("a").ThisType are the same).

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top