Many people told me that Scala does not support multiple inheritance, but recently I read the Scala language specification, I found that Scala may already support multiple inheritance.
For example, suppose we have two classes Window
and Door
, now we want to define the 3rd class called WindowDoor
, the most direct way in Scala is to use traits
instead of classes
.
The difficult part is how to deal with conflicting members.
After reading the Scala language specification, I found that Scala has a feature called static super reference
, i.e. super[C].x
that may deal with this situation.
See following code:
trait Window {
private[this] var _state: Boolean = false
def state: Boolean = _state
def state_=(value: Boolean): Unit = {
_state = value
}
def open() = {
println("Window open")
_state = true
}
}
trait Door {
private[this] var _state: Boolean = false
def state: Boolean = _state
def state_=(value: Boolean): Unit = {
_state = value
}
def open() = {
println("Door open")
_state = true
}
}
trait WindowDoor extends Window with Door {
override def state: Boolean = throw new Exception("WindowDoor state abandoned state")
override def state_= (x : Boolean) = throw new Exception("WindowDoor state_= abandoned state")
override def open() = {
println("WindowDoor open")
super[Door].open()
super[Window].open()
}
def stateWindow: Boolean = super[Window].state
def stateWindow_=(value: Boolean): Unit = {
super[Window].state_=(value)
}
def stateDoor: Boolean = super[Door].state
def stateDoor_=(value: Boolean): Unit = {
super[Door].state_=(value)
}
}
object WindowDoor {
def create(s1 : Boolean, s2: Boolean) : WindowDoor = {
val obj = new WindowDoor {}
obj.stateWindow = s1
obj.stateDoor = s2
obj
}
}
val windowDoor = WindowDoor.create(false, false)
windowDoor.open()
windowDoor.stateDoor
windowDoor.stateWindow
// Output:
// WindowDoor open
// Door open
// Window open
Note that
-
I use factory method to create object instead of
new
, because trait cannot have any “class” parameters. -
Since I use
traits
instead ofclasses
, theWindow
andDoor
evenWindowDoor
can be reused in the future.
BTW this feature does not mentioned in the book Programming in Scala.
In the Chapter 12.6 - Why not multiple inheritance?
Authors mentioned that
For example, imagine the following Scala-like code, in which super appears to be explicitly invoked on both Incrementing and Doubling:
// Multiple inheritance thought experiment
trait MyQueue extends BasicIntQueue
with Incrementing with Doubling {
def put(x: Int) = {
Incrementing.super.put(x) // (Not real Scala)
Doubling.super.put(x)
}
}
According to the authors, the above code is just pseudo-code, it does not really exist in the Scala language.
But in fact, Scala supports this kind of explicitly invoking super-trait, right? (or Scala’s designers discourage the use of static super reference
?)
super[Incrementing].put(x)
super[Doubling].put(x)
Another way to implement WindowDoor
is to separate State “classes” and Behavior “classes”.
See following code:
trait IOpen {
def open()
}
trait IOpenDefaultImpl extends IOpen {
override def open() = {
println("IOpenDefaultImpl open")
}
}
trait WindowState {
private[this] var _state: Boolean = false
def state: Boolean = _state
def state_=(value: Boolean): Unit = {
_state = value
}
}
trait WindowBehavior extends WindowState with IOpen {
abstract override def open() = {
println("WindowBehavior open")
super.open()
super[WindowState].state_=(true)
}
}
trait DoorState {
private[this] var _state: Boolean = false
def state: Boolean = _state
def state_=(value: Boolean): Unit = {
_state = value
}
}
trait DoorBehavior extends DoorState with IOpen {
abstract override def open() = {
println("DoorBehavior open")
super.open()
super[DoorState].state_=(true)
}
}
trait WindowDoorState extends WindowState with DoorState {
override def state: Boolean = throw new Exception("WindowDoorState state abandoned state")
override def state_= (x : Boolean) = throw new Exception("WindowDoorState state_= abandoned state")
def stateWindow: Boolean = super[WindowState].state
def stateWindow_=(value: Boolean): Unit = {
super[WindowState].state_=(value)
}
def stateDoor: Boolean = super[DoorState].state
def stateDoor_=(value: Boolean): Unit = {
super[DoorState].state_=(value)
}
}
trait Window extends WindowState
with IOpenDefaultImpl
with WindowBehavior
object Window {
def create(s : Boolean) : Window = {
val obj = new Window {}
obj.state = s
obj
}
}
trait Door extends DoorState
with IOpenDefaultImpl
with DoorBehavior
object Door {
def create(s : Boolean) : Door = {
val obj = new Door {}
obj.state = s
obj
}
}
trait WindowDoor extends WindowDoorState
with IOpenDefaultImpl
with WindowBehavior
with DoorBehavior
object WindowDoor {
def create(s1 : Boolean, s2: Boolean) : WindowDoor = {
val obj = new WindowDoor {}
obj.stateWindow = s1
obj.stateDoor = s2
obj
}
}
trait DoorWindow extends WindowDoorState
with IOpenDefaultImpl
with DoorBehavior
with WindowBehavior
object DoorWindow {
def create(s1 : Boolean, s2: Boolean) : DoorWindow = {
val obj = new DoorWindow {}
obj.stateWindow = s1
obj.stateDoor = s2
obj
}
}
println("----test window----")
val window = Window.create(false)
window.open()
window.state
println("----test door----")
val door = Door.create(false)
door.open()
door.state
println("----test windowDoor----")
val windowDoor = WindowDoor.create(false, false)
windowDoor.open()
windowDoor.stateDoor
windowDoor.stateWindow
println("----test doorWindow----")
val doorWindow = DoorWindow.create(false, false)
doorWindow.open()
doorWindow.stateDoor
doorWindow.stateWindow
// Output
----test window----
WindowBehavior open
IOpenDefaultImpl open
----test door----
DoorBehavior open
IOpenDefaultImpl open
----test windowDoor----
DoorBehavior open
WindowBehavior open
IOpenDefaultImpl open
----test doorWindow----
WindowBehavior open
DoorBehavior open
IOpenDefaultImpl open
The code above did two things:
-
Merge two State Traits to one State Trait and rename conflicting members by
static super reference
. -
Do stackable modifications on Behavior Traits by
override
andsuper
.
Therefore, can I think of Scala already support multiple-inheritance?
If not, which multiple-inheritance features are still lacking in Scala?
Thanks.
PS: I’m sorry I know there is already a post about multiple-inheritance, but the system prompts me that the last reply to that thread was 8 months ago. It seems tends to let me create a new one.