简体   繁体   中英

Scala generics in an abstract function

There is an abstract class Animal . Animal is extended by Dog and Cow . Animal has an abstract function copy . When called on Dog is should return Dog and when called on Cow - Cow .

abstract class Animal[T] {
  def copy[CT <: Animal[T]] (): CT
}

class Dog[T] extends Animal[T] {
  def copy = new Dog[T]()
}

This gives an error. What am I doing wrong?

Your copy method in Dog does not have the same signature as the copy method in Animal ( Animal has a type parameter and Dog does not), so the Scala compiler thinks you haven't implemented it for Dog . It looks like you're trying to work around the fact that copy should return the sub-type. You can use a self-type for this:

abstract class Animal[T] { self: T =>
  def copy: T = this
}

class Dog extends Animal[Dog]

Unless you had something else in mind for the type parameter?

It might also be more prudent to use F-bounded polymorphism in this case, to verify that T is a sub-type of Animal .

abstract class Animal[T <: Animal[T]]

Essentially CT must be invariant for your approach to work. Your concrete implementation of Dog has no control over the type of CT , for example it's impossible to account for this with your approach (hence the compiler error):

new Dog[Int]().copy[Cow[Int]]()

Your concrete implementation gives me a Dog but I wanted a Cow . This is because your copy method cannot possibly satisfy the return type parameter variance. If all you need is one return type for the copy method you could employ this alternative (inspired by the same problem as it occurs in C++):

abstract class Animal[T, SubType <: Animal[T, _]] {
  def copy (): SubType
}

class Dog[T] extends Animal[T, Dog[T]] {
  override def copy() = new Dog[T]
}

val animal: Animal[Int, Dog[Int]] = new Dog()
val dogCopy: Dog[Int] = animal.copy()

Here's another simpler (not as obvious) approach. Scala permits you to override not just the implementation of a method, but also the return type to some degree:

abstract class Animal[T] {
  def copy (): Animal[T]
}

class Dog[T] extends Animal[T] {
  override def copy() = new Dog[T]
}

val dog = new Dog[Int]()
val dogCopy: Dog[Int] = dog.copy()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM