Home Ask Login Register

Developers Planet

Your answer is one click away!

Hadil Sabbagh February 2016

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

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

Answers


m-z February 2016

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)


dth February 2016

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.

Post Status

Asked in February 2016
Viewed 3,261 times
Voted 7
Answered 2 times

Search




Leave an answer


Quote of the day: live life