如何混合一个特质到实例?
鉴于MyTrait
的特点:
trait MyTrait { def doSomething = println("boo") }
它可以被混合成一个类, extends
或with
:
class MyClass extends MyTrait
它也可以在实例化一个新实例时混合使用:
var o = new MyOtherClass with MyTrait o.doSomething
但是…可以将特质(或任何其他如果有所不同)添加到现有的实例吗?
我正在Java中使用JPA加载对象,我想用特征添加一些function给他们。 有没有可能?
我想能够混合在一个特质如下:
var o = DBHelper.loadMyEntityFromDB(primaryKey); o = o with MyTrait //adding trait here, rather than during construction o.doSomething
我有一个这样的用法的想法:
//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 }
你可以使用这个特质如下:
import MyTrait._ val a = new Test val b = a :: MyTrait b.doSomething bf
为您的示例代码:
val o = DBHelper.loadMyEntityFromDB(primaryKey) :: MyTrait o.doSomething
我希望这可以帮助你。
更新
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 }
但是这个模式有一些限制,你不能使用已经定义的隐式帮助方法。
val a = new Test af val b = a :: AnyTrait b.f1 bf 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
JVM中的现有运行时对象在堆上具有一定的大小。 给它添加一个特性将意味着改变堆的大小,并改变它的签名。
所以唯一的办法就是在编译时做一些转换。
Scala中的Mixin组合在编译时发生。 编译器可能做的是在现有对象A周围创build一个包装器B,它只需将所有调用转发给现有对象A,然后将特征T混合到B.然而,这并没有实现。 这是可能的,这是可疑的,因为对象A可能是最终类的实例,不能被扩展。
总之,mixin组合在现有的对象实例上是不可能的。
更新:
与谷果山提出的智能解决scheme相关,并将其推广到任何特性,这是我得到的。 这个想法是提取DynamicMixinCompanion
特性中常见的mixinfunction。 然后,客户端应该创build一个扩展DynamicMixinCompanion
的伴随对象,用于他想要具有dynamic混合function的每个特征。 这个伴侣对象需要定义创build的匿名特征对象( ::
。
trait DynamicMixinCompanion[TT] { implicit def baseObject[OT](o: Mixin[OT]): OT = o.obj def ::[OT](o: OT): Mixin[OT] with TT class Mixin[OT] protected[DynamicMixinCompanion](val obj: OT) } trait OtherTrait { def traitOperation = println("any trait") } object OtherTrait extends DynamicMixinCompanion[OtherTrait] { def ::[T](o: T) = new Mixin(o) with OtherTrait } object Main { def main(args: Array[String]) { val a = "some string" val m = a :: OtherTrait m.traitOperation println(m.length) } }
我通常使用一个implicit
方法将一个新的方法混合到一个现有的对象中。
看,如果我有一些代码如下:
final class Test { def f = "Just a Test" ...some other method } trait MyTrait { def doSomething = { println("boo") } } object HelperObject { implicit def innerObj(o:MixTest) = o.obj def mixWith(o:Test) = new MixTest(o) final class MixTest private[HelperObject](obj:Test) extends MyTrait }
然后您可以使用已经存在的对象Test的MyTrait
方法。
val a = new Test import HelperObject._ val b = HelperObject.mixWith(a) println(bf) b.doSomething
在你的例子中,你可以像这样使用:
import HelperObject._ val o = mixWith(DBHelper.loadMyEntityFromDB(primaryKey)); o.doSomething
我想出了一个完美的语法来定义这个HelperObject:
trait MyTrait { ..some method } object MyTrait { implicit def innerObj(o:MixTest) = o.obj def ::(o:Test) = new MixTest(o) final class MixTest private[MyTrait](obj:Test) extends MyTrait } //then you can use it val a = new Test val b = a :: MyTrait b.doSomething bf // for your example val o = DBHelper.loadMyEntityFromDB(primaryKey) :: MyTrait o.doSomething
那么隐式类呢? 对于最后一个内部类和“mixin”函数,与其他答案相比,它似乎更容易。
trait MyTrait { def traitFunction = println("trait function executed") } class MyClass { /** * This inner class must be in scope wherever an instance of MyClass * should be used as an instance of MyTrait. Depending on where you place * and use the implicit class you must import it into scope with * "import mypackacke.MyImplictClassLocation" or * "import mypackage.MyImplicitClassLocation._" or no import at all if * the implicit class is already in scope. * * Depending on the visibility and location of use this implicit class an * be placed inside the trait to mixin, inside the instances class, * inside the instances class' companion object or somewhere where you * use or call the class' instance with as the trait. Probably the * implicit class can even reside inside a package object. It also can be * declared private to reduce visibility. It all depends on the structure * of your API. */ implicit class MyImplicitClass(instance: MyClass) extends MyTrait /** * Usage */ new MyClass().traitFunction }