web-dev-qa-db-ja.com

特性をインスタンスに組み込む方法は?

特性MyTraitを指定:

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

extendsまたは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
47
Ant Kutschera

私はこの使用法について考えがあります:

//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
25
Googol Shan

JVMの既存のランタイムオブジェクトには、ヒープ上に特定のサイズがあります。それに特性を追加することは、ヒープ上のそのサイズを変更し、その署名を変更することを意味します。

したがって、実行する唯一の方法は、コンパイル時になんらかの変換を行うことです。

ScalaのMixin構成はコンパイル時に発生します。コンパイラーが実行できることは、既存のオブジェクトAへのすべての呼び出しを単純に転送する同じタイプの既存のオブジェクトAの周りにラッパーBを作成することです。特性TからBへの混合。ただし、これは実装されていません。オブジェクトAが拡張できない最終クラスのインスタンスである可能性があるため、これが可能になるかどうかは疑問です。

要約すると、既存のオブジェクトインスタンスではミックスイン構成は不可能です。

更新しました:

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)                                                                                
  }                                                                                                  
}                                                                                                    
21
axel22

私は通常、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(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
6
Googol Shan

暗黙のクラスはどうですか?最終的な内部クラスと「ミックスイン」関数を使用する他の回答の方法と比べて、私には簡単に思えます。

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

}
1
user573215

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

継承の手段としてそれを使用することは、少し面倒です。

0
Vaibhav Kumar