Self-type annotation hinders instantiation of inner class. Why?
Question
Given the abstract definitions of the Outer
class and its Inner
class I would like to instantiate the concrete Inner1
class defined within Outer1
trait.
abstract class Outer {
type Inner_Tp <: Inner;
abstract class Inner {
self: Inner_Tp =>
}
}
trait Outer1 {
self: Outer =>
protected class Inner1 extends Inner {
self: Inner_Tp =>
}
def Inner1() = new Inner1()
}
The Scala compiler prematurely terminates the compilation giving me the following error message: "error: class Inner1 cannot be instantiated because it does not conform to its self-type Outer1.this.Inner1 with Outer1.this.Inner_Tp". Why?
After all the Inner1
class is defined within an abstract context being its Outer1
trait. I would like to postpone the definition of type Inner_Tp
until the trait gets mixed into some concrete class.
Solution
For Inner1
, the self-type says that it will always be instantiated together with the abstract type Inner_Tp
. That's a promise that is not fulfilled at the instantiation point: the type is only Inner1
instead of Inner1 with Inner_Tp
.
If you need to delay the definition of Inner_Tp
, you also need to delay the creation of any instances that have it as a self-type. That is absolutely necessary, since you cannot produce a value of a type that you don't know yet. So better leave the method abstract. You may refine the abstract type as well:
trait Outer1 extends Outer {
type Inner_Tp <: Inner1
protected class Inner1 extends Inner
def Inner1(): Inner_Tp
}
I'm not sure what you are after, but you may not need the self-types at all (I left them out for brevity already).
OTHER TIPS
Because the self: Inner_Tp
tells that no subtype of Inner
will be allowed to be instanciated unless it is a subtype of Inner_Tp
too.
Rewriting self => Inner_Tp
in Inner1
just restates the constraint, it does not satisfy it. To get a similar idea, when you have a descendant of an abstract class, if it does not implement the abstract method, you must write again that it is abstract. And you cannot instanciate. Same here, you restated that Inner_Tp
is required, you did not make Inner_Tp
available.
As long as Inner_Tp
is an abstract type, you will not be able to mix it in, and so you will not be able to write new Inner1
. You should probably introduce an abstract method that create Inners.