Wie mischen in einem Zug zu Beispiel?
Frage
Bei einem Merkmal MyTrait
:
trait MyTrait {
def doSomething = println("boo")
}
es kann mit extends
oder with
in eine Klasse gemischt werden
class MyClass extends MyTrait
Es kann auch bei der Instanziierung eine neue Instanz gemischt werden:
var o = new MyOtherClass with MyTrait
o.doSomething
Aber ... kann das Merkmal (oder eine andere, wenn das einen Unterschied macht) zu einer vorhandenen Instanz hinzugefügt werden?
Ich bin Laden von Objekten unter Verwendung von JPA in Java, und ich möchte einige Funktionen, um sie hinzuzufügen Eigenschaften verwenden. Ist es möglich?
Ich möchte in der Lage sein, in einem Zug zu mischen, wie folgt:
var o = DBHelper.loadMyEntityFromDB(primaryKey);
o = o with MyTrait //adding trait here, rather than during construction
o.doSomething
Lösung
Ich habe eine Idee für diese Nutzung:
//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
}
Sie können diese Eigenschaft verwenden, wie unten:
import MyTrait._
val a = new Test
val b = a :: MyTrait
b.doSomething
b.f
für Ihren Beispielcode:
val o = DBHelper.loadMyEntityFromDB(primaryKey) :: MyTrait
o.doSomething
Ich hoffe, das Ihnen helfen kann.
AKTUALISIERT
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
}
aber dieses Muster einige beschränken hat, kann man nicht einige implizite Hilfsmethode verwenden, die bereits definiert.
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
Andere Tipps
Ein bestehendes Laufzeitobjekt in der JVM hat eine bestimmte Größe auf dem Heap. Hinzufügen einer Eigenschaft zu es bedeuten würde, seine Größe auf dem Heap zu verändern und das Ändern seiner Unterschrift.
So ist der einzige Weg wäre, zu gehen, eine Art von Transformation bei der Kompilierung zu tun.
Mixin Zusammensetzung in Scala tritt bei der Kompilierung. Was für Compiler könnte möglicherweise tun, ist ein Wrapper-B um ein vorhandenes Objekt A mit der gleichen Art zu erstellen, die einfach leitet alle Anrufe an das bestehende Objekt A, und dann in einem Zug T auf B. Das aber mischen, ist nicht implementiert. Es ist fraglich, wenn dies möglich wäre, da das Objekt A eine Instanz einer endgültigen Klasse sein könnte, die nicht verlängert werden können.
Insgesamt mixin Zusammensetzung ist nicht möglich, auf bestehende Objektinstanzen.
AKTUALISIERT:
Bezogen auf die intelligente Lösung von Googol Shan vorgeschlagen, und es an die Arbeit mit jedem Zug zu verallgemeinern, ist dies so weit wie ich habe. Die Idee ist es, die gemeinsame mixin Funktionalität im DynamicMixinCompanion
Merkmale zu extrahieren. Der Kunde sollte dann ein Begleitobjekt erstreckt DynamicMixinCompanion
für jedes Merkmal erstellen will er für die dynamische mixin Funktionalität haben. Dieser Begleiter Objekt erfordert die Definition des anonyme Merkmal Objekt wird (::
) erstellt.
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)
}
}
Früher habe ich in der Regel eine implicit
in einem neuen Verfahren auf ein vorhandenes Objekt zu mischen.
Siehe, wenn ich habe einige Code wie folgt:
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
}
und dann können Sie MyTrait
Methode mit einem bereits vorhandenen Objekt-Test verwenden.
val a = new Test
import HelperObject._
val b = HelperObject.mixWith(a)
println(b.f)
b.doSomething
in Ihrem Beispiel, können Sie wie folgt verwenden:
import HelperObject._
val o = mixWith(DBHelper.loadMyEntityFromDB(primaryKey));
o.doSomething
Ich denke einen Präfekten Syntax aus dieser HelperObject zu definieren:
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
Was ist eine implizite Klasse? Es scheint einfacher zu mir im Vergleich zu der Art und Weise in den anderen Antworten mit einer abschließenden inneren Klasse und einer „mixin“ -Funktion.
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
}
Warum verwenden nicht Scala meiner Bibliothek Muster verlängern?
https://alvinalexander.com/scala/scala-2.10-implicit -Klasse-Beispiel
Ich bin mir nicht sicher, was ist der Rückgabewert von:
var o = DBHelper.loadMyEntityFromDB(primaryKey);
aber lassen Sie uns sagen, es DBEntity
für unser Beispiel. Sie können die Klasse DBEntity nehmen und es zu einer Klasse umwandeln, die Ihre Eigenschaft erstreckt, MyTrait
.
So etwas wie:
trait MyTrait {
def doSomething = {
println("boo")
}
}
class MyClass() extends MyTrait
// Have an implicit conversion to MyClass
implicit def dbEntityToMyClass(in: DBEntity): MyClass =
new MyClass()
Ich glaube, man könnte auch dies vereinfachen, indem nur eine implizite Klasse.
implicit class ConvertDBEntity(in: DBEntity) extends MyTrait
Ich mag nicht besonders die akzeptierte Antwort hier, b / c den ::
Betreiber Überlastungen ein Merkmal zu in mischen.
In Scala, der ::
Operator für Sequenzen verwendet wird, das heißt:.
val x = 1 :: 2 :: 3 :: Nil
Mit ihm als Mittel der Vererbung fühlt, IMHO, ein wenig umständlich.