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

发布于 2024-09-26 09:07:19 字数 619 浏览 7 评论 0原文

给定一个特征 MyTrait

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

它可以与 extendswith 混合到一个类中:

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

暖树树初阳… 2024-10-03 09:07:20

我对此用法有一个想法:

//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

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
給妳壹絲溫柔 2024-10-03 09:07:20

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

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

Scala 中的 Mixin 组合发生在编译时。编译器可能会做的是围绕具有相同类型的现有对象 A 创建一个包装器 B,该包装器 B 简单地将所有调用转发到现有对象 A,然后将特征 T 混合到 B。但是,这并未实现。这何时可能是值得怀疑的,因为对象 A 可能是最终类的实例,而该类无法扩展。

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

更新:

与 Googol Shan 提出的智能解决方案相关,并将其推广到任何特征,这是我所得到的。这个想法是在DynamicMixinCompanion特征中提取常见的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)                                                                                
  }                                                                                                  
}                                                                                                    

An existing runtime object in the JVM has a certain size on the heap. Adding a trait to it would mean altering its size on the heap, and changing its signature.

So the only way to go would be to do some kind of transformation at compile time.

Mixin composition in Scala occurs at compile time. What compiler could potentially do is create a wrapper B around an existing object A with the same type that simply forwards all calls to the existing object A, and then mix in a trait T to B. This, however, is not implemented. It is questionable when this would be possible, since the object A could be an instance of a final class, which cannot be extended.

In summary, mixin composition is not possible on existing object instances.

UPDATED:

Related to the smart solution proposed by Googol Shan, and generalizing it to work with any trait, this is as far as I got. The idea is to extract the common mixin functionality in the DynamicMixinCompanion trait. The client should then create a companion object extending DynamicMixinCompanion for each trait he wants to have the dynamic mixin functionality for. This companion object requires defining the anonymous trait object gets created (::).

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)                                                                                
  }                                                                                                  
}                                                                                                    
风柔一江水 2024-10-03 09:07:20

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

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

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 方法与现有的对象 Test 一起使用。

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

I usually used a implicit to mix in a new method to an existing object.

See, if I have some code as below:

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
}

and then you can use MyTrait method with an already existing object Test.

val a = new Test
import HelperObject._
val b = HelperObject.mixWith(a)
println(b.f)
b.doSomething

in your example, you can use like this:

import HelperObject._
val o = mixWith(DBHelper.loadMyEntityFromDB(primaryKey));
o.doSomething

I am thinking out a prefect syntax to define this 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
2024-10-03 09:07:20

隐式类怎么样?与其他答案中使用最终内部类和“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

}

What about an implicit class? It seems easier to me compared to the way in the other answers with a final inner class and a "mixin"-function.

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

}
清秋悲枫 2024-10-03 09:07:20

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

https://alvinalexander.com/scala/scala-2.10-implicit-class-示例

我不确定返回值是什么:


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

使用它作为继承的手段,恕我直言,有点尴尬。

Why not use Scala's extend my library pattern?

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

I'm not sure what the return value is of:


var o = DBHelper.loadMyEntityFromDB(primaryKey);

but let us say, it is DBEntity for our example. You can take the class DBEntity and convert it to a class that extends your trait, MyTrait.

Something like:

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

class MyClass() extends MyTrait

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

I believe you could also simplify this by just using an implicit class.

implicit class ConvertDBEntity(in: DBEntity) extends MyTrait

I particularly dislike the accepted answer here, b/c it overloads the :: operator to mix-in a trait.

In Scala, the :: operator is used for sequences, i.e.:

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

Using it as a means of inheritance feels, IMHO, a little awkward.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文