48

给定一个特征MyTrait

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

它可以与extendsor混合到一个类中with

class MyClass extends MyTrait

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

var o = new MyOtherClass with MyTrait
o.doSomething

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

我正在使用 Java 中的 JPA 加载对象,并且我想使用特征向它们添加一些功能。有可能吗?

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

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

5 回答 5

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
}

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

import MyTrait._

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

对于您的示例代码:

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
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
于 2010-10-09T15:45:07.127 回答
22

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

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

Scala 中的 Mixin 组合发生在编译时。编译器可能做的是围绕现有对象 A 创建一个具有相同类型的包装器 B,该包装器仅将所有调用转发到现有对象 A,然后将特征 T 混合到 B。但是,这没有实现。什么时候可以做到这一点值得怀疑,因为对象 A 可能是 final 类的一个实例,它不能被扩展。

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

更新:

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

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)                                                                                
  }                                                                                                  
}                                                                                                    
于 2010-10-08T18:43:24.073 回答
7

我通常使用 aimplicit将新方法混合到现有对象中。

看看,如果我有一些代码如下:

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
}

然后您可以将MyTrait方法与已经存在的对象测试一起使用。

val a = new Test
import HelperObject._
val b = HelperObject.mixWith(a)
println(b.f)
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
b.f
// for your example
val o = DBHelper.loadMyEntityFromDB(primaryKey) :: MyTrait
o.doSomething
于 2010-10-09T09:24:07.300 回答
1

那么隐式类呢?与具有最终内部类和“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

}
于 2016-07-22T11:29:44.030 回答
0

为什么不使用 Scala 的扩展我的库模式?

https://alvinalexander.com/scala/scala-2.10-implicit-class-example

我不确定返回值是什么:

var o = DBHelper.loadMyEntityFromDB(primaryKey);

但是让我们说,这是DBEntity为了我们的例子。您可以获取类 DBEntity 并将其转换为扩展您的特征的类,MyTrait.

就像是:

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

class MyClass() extends MyTrait

// Have an implicit conversion to MyClass
implicit def dbEntityToMyClass(in: DBEntity): MyClass = 
new MyClass()

我相信你也可以通过使用隐式类来简化这一点。

implicit class ConvertDBEntity(in: DBEntity) extends MyTrait

我特别不喜欢这里接受的答案,因为它使::运算符重载以混合特征。

在 Scala 中,::运算符用于序列,即:

val x = 1 :: 2 :: 3 :: Nil

恕我直言,将其用作继承方式感觉有点尴尬。

于 2018-07-20T03:39:38.987 回答