2010-10-08 5 views
43

Учитывая черта MyTrait:Как смешивать черту с примером?

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

может быть смешан в класс с extends или with:

class MyClass extends MyTrait 

Он также может быть смешано на инстанцировании новый экземпляр:

var o = new MyOtherClass with MyTrait 
o.doSomething 

Но ... может ли черта (или любое другое, если это имеет значение) добавляться к существующим ins стояние?

Я загружаю объекты с использованием JPA в Java, и я хотел бы добавить к ним некоторые функции, используя черты. Это вообще возможно?

Я хотел бы быть в состоянии смешать в черте следующим образом:

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

ответ

25

У меня есть идея для такого использования:

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

I надеюсь, это может вам помочь.

ОБНОВЛЕНО

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 
+2

Это очень полезная фетрика, когда вы определили метод с 'implicit' и импортируете этот метод в свою область действия, этот метод может помочь вам передать объект, который указывает аргументом метода другому объекту, который указывает методом return, когда вы необходимо вызвать метод последнего, который не определен в первом. –

+2

Очень приятное решение, мне оно нравится. Интересно, как легко это можно было бы сделать общим? Возможно, добавьте общий параметр в '::' в объект 'MyTrait', чтобы он мог работать для любого типа. Можно ли также заставить работать с произвольными чертами, которые мы хотим смешивать ...? – axel22

+0

@ axel22 да, я думаю, это можно сделать родовым, как мой обновленный ответ. но я не могу заставить его работать с произвольной чертой, я новичок в scala. –

20

Существующий объект выполнения в JVM имеет определенный размер в куче. Добавление признака к нему означало бы изменение его размера в куче и изменение его подписи.

Таким образом, единственный способ пойти - это сделать какое-то преобразование во время компиляции.

Состав смесина в Scala происходит во время компиляции. Компилятор потенциально может создать оболочку B вокруг существующего объекта A с тем же типом, который просто перенаправляет все вызовы на существующий объект A, а затем смешивается с признаком T от B. Это, однако, не реализовано. Это сомнительно, когда это было бы возможно, поскольку объект А может быть экземпляром конечного класса, который не может быть расширен.

Таким образом, композиция смесина невозможна в существующих экземплярах объектов.

ОБНОВЛЕН:

, относящийся к интеллектуальному решению, предложенному гугол Шана, и обобщая его для работы с любым признаком, это, насколько я получил. Идея состоит в том, чтобы извлечь общую функциональность mixin в свойстве DynamicMixinCompanion. Затем клиент должен создать объект-компаньон, расширяющий DynamicMixinCompanion для каждой цели, для которой он хочет иметь динамическую функциональность mixin. Этот объект-компаньон требует определения создания анонимного объекта-объекта (::).

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)                     
    }                         
}                          
+0

Как незначительное замечание для пояснения: переменная 'm' является экземпляром' OtherTrait', но _not_ экземпляром 'String'. (Это «неявный», который «преобразует» его обратно в строку всякий раз, когда это необходимо, во время компиляции.) Это можно увидеть, добавив 'println (« m - экземпляр String/OtherTrait: »+ m.isInstanceOf [String ] + "/" + m.isInstanceOf [OtherTrait]) 'в конце функции' main'. – Hbf

+0

@ axel22, если я правильно понимаю, таким образом вы можете смешать в каком-то экземпляре черту с поведением (у которой есть некоторые определения). Но не в состоянии смешать в черту, которая имеет и некоторые ценности, не так ли? –

5

Я обычно использовал implicit смешать в новом методе к существующему объекту.

See, если у меня есть некоторый код, как показано ниже:

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

} 
+0

Хорошо, но с вашим решением, Черты могут быть привязаны только к экземплярам, ​​созданным с новыми и в области. Несколько раз вы хотите привязать объект к объекту, созданному где-то еще, например. от уровня ORM –

 Смежные вопросы

  • Нет связанных вопросов^_^