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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3893274/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 02:29:08  来源:igfitidea点击:

How to mix-in a trait to instance?

scalatraits

提问by Ant Kutschera

Given a trait MyTrait:

给定一个特征MyTrait

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

it can be mixed into a class with extendsor with:

它可以与extends或混合到一个类中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?

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

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

采纳答案by Googol Shan

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

回答by axel22

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.

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

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.

Scala 中的 Mixin 组合发生在编译时。编译器可能会做的是围绕具有相同类型的现有对象 A 创建一个包装器 B,它将所有调用简单地转发到现有对象 A,然后将特征 T 混合到 B。然而,这并未实现。这什么时候可能是有问题的,因为对象 A 可能是一个不能扩展的 final 类的实例。

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

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

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 DynamicMixinCompaniontrait. The client should then create a companion object extending DynamicMixinCompanionfor each trait he wants to have the dynamic mixin functionality for. This companion object requires defining the anonymous trait object gets created (::).

与 Googol Shan 提出的智能解决方案相关,并将其推广到任何特征,这是我得到的。这个想法是在特征中提取常见的混合功能DynamicMixinCompanion。然后,客户端应该创建一个伴随对象,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)                                                                                
  }                                                                                                  
}                                                                                                    

回答by Googol Shan

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

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

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 MyTraitmethod with an already existing object Test.

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

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:

我正在想一个完美的语法来定义这个 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

回答by user573215

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.

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

}

回答by Vaibhav Kumar

Why not use Scala's extend my library pattern?

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

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

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);

var o = DBHelper.loadMyEntityFromDB(primaryKey);

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

但让我们说,这是DBEntity我们的例子。您可以使用类 DBEntity 并将其转换为扩展您的 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.

我特别不喜欢这里接受的答案,b/c 它使::操作符过载以混合特征。

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

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

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

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

使用它作为继承的方式感觉,恕我直言,有点尴尬。