return class extending generic trait in Scala

492 Views Asked by At

Scala code:

trait Converter[S, T] {
  def convert(source: S): T
}

class StringDuplicatorConverter extends Converter[Integer, String] {
  override def convert(source: Integer): String = {
    source.toString + source.toString
  }
}

// whatever, the point is to show potentially many 'converters'
// from Integer (for example) to something
class SomeOtherConverter extends Converter[Integer, User] {
  override def convert(source: Integer): User = {
    User(source)
  }
}

trait ConverterProvider {
  def getConverter[N]: Converter[Integer, N]
}

class MyClass extends ConverterProvider {
  override def getConverter[N]: Converter[Integer, N] = {
    new StringDuplicatorConverter()
  }
}

gives

Error:(17, 5) type mismatch;
 found   : StringDuplicatorConverter
 required: Converter[Integer,N]
    new StringDuplicatorConverter()
2

There are 2 best solutions below

0
On BEST ANSWER

It may be that what you really want is for each ConverterProvider to provide a converter to a specific type (otherwise the definition of MyClass doesn't make much sense: it should return different converters for different N, not always StringDuplicatorConverter). If so, the correct definition is

trait ConverterProvider[N] {
  def getConverter: Converter[Integer, N]
}

class MyClass extends ConverterProvider[String] {
  override def getConverter: Converter[Integer, String] = {
    new StringDuplicatorConverter()
  }
}
0
On

Yes. A call to getConverter[N] is supposed to return something of type Converter[Integer,N] but StringDuplicatorConverter is type Converter[Integer,String]. Since N is not restricted to String, and thus they are different types, this won't compile.

If the compiler were given some guarantee that N is a, or super-type of, String then it would work. This can be done by making the return type covariant ...

trait Converter[S, +T] { ...

... and then defining getConverter, and the override, like this:

def getConverter[N >: String]: Converter[Integer, N]

Now it compiles and appears to work.

val mc = new MyClass
mc.getConverter.convert(7)  // res0: String = 77