How to mix-in a trait to instance?

Ant Kutschera picture Ant Kutschera · Oct 8, 2010 · Viewed 24.3k times · Source

Given a trait MyTrait:

trait MyTrait {
  def doSomething = println("boo")
}

it can be mixed into a class with extends or with:

class MyClass extends MyTrait

It can also be mixed upon instantiating a new instance:

var o = new MyOtherClass with MyTrait
o.doSomething

But...can the trait (or any other if that makes a difference) be added to an existing instance?

I'm loading objects using JPA in Java and I'd like to add some functionality to them using traits. Is it possible at all?

I'd like to be able to mix in a trait as follows:

var o = DBHelper.loadMyEntityFromDB(primaryKey);
o = o with MyTrait //adding trait here, rather than during construction
o.doSomething

Answer

Googol Shan picture Googol Shan · Oct 9, 2010

I have a idea for this usage:

//if I had a class like this
final class Test {
  def f = println("foo")
}
trait MyTrait {
  def doSomething = {
    println("boo")
  }
}
object MyTrait {
  implicit def innerObj(o:MixTest) = o.obj

  def ::(o:Test) = new MixTest(o)
  final class MixTest private[MyTrait](val obj:Test) extends MyTrait
}

you can use this trait as below:

import MyTrait._

val a = new Test
val b = a :: MyTrait
b.doSomething
b.f

for your example code:

val o = DBHelper.loadMyEntityFromDB(primaryKey) :: MyTrait
o.doSomething

I hope this can help you.

UPDATED

object AnyTrait {
  implicit def innerObj[T](o: MixTest[T]):T = o.obj

  def ::[T](o: T) = new MixTest(o)
  final class MixTest[T] private[AnyTrait](val obj: T) extends MyTrait
}

but this pattern has some restrict, you can't use some implicit helper method that defined already.

val a = new Test
a.f
val b = a :: AnyTrait
b.f1
b.f
val c = "say hello to %s" :: AnyTrait
println(c.intern)  // you can invoke String's method 
println(c.format("MyTrait"))  //WRONG. you can't invoke StringLike's method, though there defined a implicit method in Predef can transform String to StringLike, but implicit restrict one level transform, you can't transform MixTest to String then to StringLike.
c.f1
val d = 1 :: AnyTrait
println(d.toLong)
d.toHexString // WRONG, the same as above
d.f1