What is the difference between mixins and inheritance?
I think its very usage dependent. Scala being a multi-paradigm language makes it powerful as well as a bit confusing at times. I think Mixins are very powerful when used the right way. Mixins should be used to introduce behavior and reduce bolierplate.
A trait in Scala can have implementations and it is tempting to extend them and use them.
Traits could be used for inheritance. It can also be called mixins however that in my opinion is not the best way to use mixin
behavior. In this case you could think of traits as Java Abstract Classes. Wherein you get subclasses that are "type of" the super class (the trait).
However Traits could be used as proper mixins
as well. Now using a trait as a mixin
depends on the implementation that is "how you mix it in". Mostly its a simple question to ask yourself . It is "Is the subclass of the trait truly a kind
of the trait or are the behaviors in the trait behaviors that reduce boilerplate".
Typically it is best implemented by mixing in traits to objects rather than extending the trait to create new classes.
For example consider the following example:
//All future versions of DAO will extend this
trait AbstractDAO{
def getRecords:String
def updateRecords(records:String):Unit
}
//One concrete version
trait concreteDAO extends AbstractDAO{
override def getRecords={"Here are records"}
override def updateRecords(records:String){
println("Updated "+records)
}
}
//One concrete version
trait concreteDAO1 extends AbstractDAO{
override def getRecords={"Records returned from DAO2"}
override def updateRecords(records:String){
println("Updated via DAO2"+records)
}
}
//This trait just defines dependencies (in this case an instance of AbstractDAO) and defines operations based over that
trait service{
this:AbstractDAO =>
def updateRecordsViaDAO(record:String)={
updateRecords(record)
}
def getRecordsViaDAO={
getRecords
}
}
object DI extends App{
val wiredObject = new service with concreteDAO //injecting concrete DAO to the service and calling methods
wiredObject.updateRecords("RECORD1")
println(wiredObject.getRecords)
val wiredObject1 = new service with concreteDAO1
wiredObject1.updateRecords("RECORD2")
println(wiredObject1.getRecords)
}
concreteDAO
is a trait which extends AbstractDAO
- This is inheritance
val wiredObject = new service with concreteDAO
-
This is proper mixin behavior
Since the service trait mandates the mixin
of a AbstractDAO
. It would be just wrong for Service
to extend ConcreteDAO
anyways because the service
required AbstractDAO
it is not a type of AbstractDAO
.
Instead you create instances of service
with different mixins.
I'm not sure I understood your question properly, but if I did, you're asking how something can inherit without really meaning the same thing as inheriting.
Mixins, however, aren't inheritance – it's actually more similar to dynamically adding a set of methods into an object. Whereas inheritance says "This thing is a kind of another thing", mixins say, "This object has some traits of this other thing." You can see this in the keyword used to declare mixins: trait
.
To blatantly steal an example from the Scala homepage:
abstract class Spacecraft {
def engage(): Unit
}
trait CommandoBridge extends Spacecraft {
def engage(): Unit = {
for (_ <- 1 to 3)
speedUp()
}
def speedUp(): Unit
}
trait PulseEngine extends Spacecraft {
val maxPulse: Int
var currentPulse: Int = 0
def speedUp(): Unit = {
if (currentPulse < maxPulse)
currentPulse += 1
}
}
class StarCruiser extends Spacecraft
with CommandoBridge
with PulseEngine {
val maxPulse = 200
}
In this case, the StarCruiser
isn't a CommandoBridge
or PulseEngine
; it has them, though, and gains the methods defined in those traits. It is a Spacecraft
, as you can see because it inherits from that class.
It's worth mentioning that when a trait
extends a class
, if you want to make something with
that trait, it has to extend that class. For example, if I had a class Dog
, I couldn't have a Dog with PulseEngine
unless Dog
extended Spacecraft
. In that way, it's not quite like adding methods; however, it's still similar.
A trait (which is called mixin when mixed with a class) is like an interface in Java (though there are many differences) where you can add additional features to a class without necessarily having "is a" relationship. Or you can say that generally traits bundle up features which can be used by multiple independent classes.
To give you an example from Scala library, Ordered[A] is a trait
which provides implementation for some basic comparison operations (like <
, <=
, >
, >=
) to classes that can have data with natural ordering.
For example, let's say you have your own class Number
and subclasses EvenNumber
and OddNumber
as shown below.
class Number(val num : Int) extends Ordered[Number] {
override def compare(that : Number) = this.num - that.num
}
trait Half extends Number {
def half() = num / 2
}
trait Increment extends Number {
def increment() = num + 1
}
class EvenNumber(val evenNum : Int) extends Number(evenNum) with Half
class OddNumber(val oddNum : Int) extends Number(oddNum) with Increment
In the example above, classes EvenNumber
and OddNumber
share is a relationship with Number
but EvenNumber
does not have "is a" relation with Half
neither OddNumber
share "is a" relation with Increment
.
Another important point is even though class Number
uses extends Ordered
syntax, it means that Number
has an implicit is a relationship with superclass of Ordered
ie Any
.