Scala: illegal inheritance; self-type Foo[T] does not conform to Foo[T]'s selftype T

493 Views Asked by At

I have the following code snippet:

abstract class Foo[T <: Foo[T]] { self: T =>
  def bar(x: T): T
  def newFoo: Foo[T] = {
    new Foo[T] { self: T =>
      // ...
    }
 }

}

I have a need to generate a new instance of Foo within a method of my abstract class. Can anyone advise me on how best to approach this?

Thanks, Hadil

2

There are 2 best solutions below

3
On

The self-type self: T => implies that your Foo[T] must also be a T. new Foo[T] { ... } isn't an instance of T for any arbitrary T that makes up Foo[T]. You also can't add a self-type to an anonymous class like new Foo[T] { ... }, because it doesn't make sense. Either the concrete class is or isn't a T at that point.

Constructing a method like def newFoo: Foo[T] in a type-safe way isn't really possible with the self-type in place, because you'd need to know how to construct an arbitrary T. You might be able to do what you want with reflection, when each T has the same constructor.

import scala.reflect._

abstract class Foo[T <: Foo[T] : ClassTag] { self: T =>
  def bar(x: T): T
  def newFoo: Foo[T] = classTag[T].runtimeClass.newInstance.asInstanceOf[T]
}

class Bar extends Foo[Bar] {
  def bar(x: Bar): Bar = x
}

scala> val b = new Bar
b: Bar = Bar@2a2d45ba

scala> b.newFoo
res1: Foo[Bar] = Bar@146ba0ac

This ceases to work when there are constructor parameters:

case class Baz(i: Int) extends Foo[Baz] {
  def bar(x: Baz): Baz = x
}

scala> val baz = new Baz(0)
baz: Baz = Baz(0)

scala> baz.newFoo
java.lang.InstantiationException: Baz
  at java.lang.Class.newInstance(Class.java:427)
  at Foo.newFoo(<console>:16)
0
On

Well, you do not know the concrete class where Foo will be inherited in the future. So especially, you do not know which constructor parameters this class will have and how to supply arguments for them. If you really would want to do this, you would have to make some assumptions (that you cannot enforce at compile time) to achieve this.

So what you probably should do, is, leave this method abstract and implement it in a subclass. If this is not an option, you probably have some issues with your overall class design. Better present here what you want to model and ask for help.

If you assume, that the constructor of the concrete class won't have any parameters, you can implement newFoo just as

def newFoo = this.getClass.newInstance

There is no need for classTags or other fancy stuff.