如何将特征混合到实例中?

Ant*_*era 47 scala traits

鉴于一个特点MyTrait:

trait MyTrait {
  def doSomething = println("boo")
}
Run Code Online (Sandbox Code Playgroud)

它可以与extendsor 混合成一个类with:

class MyClass extends MyTrait
Run Code Online (Sandbox Code Playgroud)

它也可以在实例化一个新实例时混合:

var o = new MyOtherClass with MyTrait
o.doSomething
Run Code Online (Sandbox Code Playgroud)

但是......可以将特征(或任何其他如果产生影响的特征)添加到现有实例中吗?

我在Java中使用JPA加载对象,我想使用traits为它们添加一些功能.有可能吗?

我希望能够按如下方式混合特征:

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

Goo*_*han 26

我有这个用法的想法:

//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
}
Run Code Online (Sandbox Code Playgroud)

你可以使用这个特性如下:

import MyTrait._

val a = new Test
val b = a :: MyTrait
b.doSomething
b.f
Run Code Online (Sandbox Code Playgroud)

为您的示例代码:

val o = DBHelper.loadMyEntityFromDB(primaryKey) :: MyTrait
o.doSomething
Run Code Online (Sandbox Code Playgroud)

我希望这可以帮到你.

更新

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
}
Run Code Online (Sandbox Code Playgroud)

但是这种模式有一些限制,你不能使用已经定义的一些隐式辅助方法.

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
Run Code Online (Sandbox Code Playgroud)

  • 这是一个非常有用的方法,当你使用`implicit`定义一个方法,并在你的范围中导入这个方法时,这个方法可以帮助你将方法参数指定的对象传递给另一个对象,该对象在你需要时通过方法返回调用后者未在前者定义的方法. (2认同)
  • 非常好的解决方案,我喜欢它.我想知道它是否也可以变得通用 - 可能在`MyTrait`对象中为`::`添加一个泛型参数可以允许它适用于任何类型.它是否也可以与我们想混合的任意特征一起工作......? (2认同)

axe*_*l22 22

JVM中的现有运行时对象在堆上具有特定大小.添加特征将意味着改变其在堆上的大小,并改变其签名.

因此,唯一的方法是在编译时进行某种转换.

Scala中的Mixin组合发生在编译时.编译器可能做的是在现有对象A周围创建一个包装器B,其类型相同,只是将所有调用转发到现有对象A,然后将特征T混合到B.但是,这并没有实现.如果可能的话,这是值得怀疑的,因为对象A可能是最终类的一个实例,它不能被扩展.

总之,mixin组合在现有对象实例上是不可能的.

更新:

与Googol Shan提出的智能解决方案相关,并将其推广到任何特性,这是我所得到的.我们的想法是在特征中提取常见的mixin功能DynamicMixinCompanion.然后,客户端应该DynamicMixinCompanion为他想要具有动态mixin功能的每个特征创建一个扩展的伴随对象.此伴随对象需要定义创建的匿名特征对象(::).

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)                                                                                
  }                                                                                                  
}                                                                                                    
Run Code Online (Sandbox Code Playgroud)


Goo*_*han 7

我通常使用a 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
}
Run Code Online (Sandbox Code Playgroud)

然后你可以使用MyTrait已经存在的对象Test的方法.

val a = new Test
import HelperObject._
val b = HelperObject.mixWith(a)
println(b.f)
b.doSomething
Run Code Online (Sandbox Code Playgroud)

在您的示例中,您可以像这样使用:

import HelperObject._
val o = mixWith(DBHelper.loadMyEntityFromDB(primaryKey));
o.doSomething
Run Code Online (Sandbox Code Playgroud)

我正在考虑一个完美的语法来定义这个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
b.f
// for your example
val o = DBHelper.loadMyEntityFromDB(primaryKey) :: MyTrait
o.doSomething
Run Code Online (Sandbox Code Playgroud)