2010-10-08 13 views
43

एक विशेषता MyTrait को देखते हुए:उदाहरण के लिए एक विशेषता में मिश्रण कैसे करें?

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

यह extends या with के साथ एक कक्षा में मिलाया जा सकता है:

class MyClass extends MyTrait 

यह भी एक नया उदाहरण instantiating पर मिलाया जा सकता है:

var o = new MyOtherClass with MyTrait 
o.doSomething 

लेकिन ... विशेषता (या कोई अन्य अगर कोई फर्क पड़ता है) एक मौजूदा इन्स में जोड़ा जा सकता है हैैं?

मैं जावा में जेपीए का उपयोग कर वस्तुओं को लोड कर रहा हूं और मैं लक्षणों का उपयोग करके उनके लिए कुछ कार्यक्षमता जोड़ना चाहता हूं। क्या यह सभी के लिए संभव है?

मैं इस प्रकार एक विशेषता में मिश्रण करने में सक्षम होना चाहते हैं:

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

उत्तर

25

मैं इस प्रयोग के लिए एक विचार है आशा है इससे आपको सहायता मिलेगी।

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

यह एक बहुत ही उपयोगी भ्रूण है, जब आपने 'निहित' के साथ एक विधि परिभाषित की है, और इस विधि को अपने दायरे में आयात किया है, तो यह विधि विधि ऑब्जेक्ट को निर्दिष्ट करने में आपकी मदद कर सकती है जो विधि तर्क द्वारा निर्दिष्ट किसी अन्य ऑब्जेक्ट को निर्दिष्ट करती है बाद की विधि को आह्वान करने की आवश्यकता है जिसे पूर्व में परिभाषित नहीं किया गया है। –

+2

बहुत अच्छा समाधान, मुझे यह पसंद है। मुझे आश्चर्य है कि इसे सामान्य रूप से कितना आसानी से बनाया जा सकता है - शायद 'मायट्रेट' ऑब्जेक्ट में '::' के लिए एक सामान्य पैरामीटर जोड़ें, इसे किसी भी प्रकार के लिए काम करने की अनुमति दे सकता है। क्या इसे मनमाना लक्षणों के साथ काम करने के लिए भी बनाया जा सकता है जिसे हम मिश्रण करना चाहते हैं ...? – axel22

+0

@ axel22 हाँ, मुझे लगता है कि इसे मेरे अद्यतन उत्तर की तरह सामान्य बनाया जा सकता है। लेकिन मैं इसे मनमाना विशेषता के साथ काम करने के लिए नहीं बनाया जा सकता, मैं स्कैला के लिए नौसिखिया हूँ। –

20

एक मौजूदा JVM में क्रम वस्तु ढेर पर एक निश्चित आकार है। इसमें एक विशेषता जोड़ने से ढेर पर इसका आकार बदलना होगा, और इसके हस्ताक्षर बदलना होगा।

तो जाने का एकमात्र तरीका संकलन समय पर किसी प्रकार का परिवर्तन करना होगा।

स्कैला में मिक्सिन संरचना संकलन समय पर होती है। संभावित कंपाइलर संभावित रूप से क्या कर सकता है एक मौजूदा ऑब्जेक्ट ए के आस-पास एक रैपर बी बना सकता है, उसी प्रकार के साथ जो केवल मौजूदा ऑब्जेक्ट ए को सभी कॉल अग्रेषित करता है, और फिर एक विशेषता टी से बी में मिलाता है। हालांकि, यह लागू नहीं किया गया है। जब यह संभव होगा, यह संदिग्ध है, क्योंकि ऑब्जेक्ट ए अंतिम श्रेणी का उदाहरण हो सकता है, जिसे विस्तारित नहीं किया जा सकता है।

संक्षेप में, मौजूदा ऑब्जेक्ट उदाहरणों पर मिश्रित संरचना संभव नहीं है।

UPDATED:

स्मार्ट समाधान गोगोल शान द्वारा प्रस्तावित करने के लिए संबंधित, और किसी भी लक्षण के साथ काम करने के लिए इसे सामान्यीकरण, यह जहाँ तक मुझे मिल गया है। विचार DynamicMixinCompanion विशेषता में सामान्य मिश्रित कार्यक्षमता निकालने के लिए है। क्लाइंट को प्रत्येक गुण के लिए DynamicMixinCompanion का विस्तार करने वाला एक साथी ऑब्जेक्ट बनाना चाहिए, जिसके लिए वह गतिशील मिश्रण कार्यक्षमता चाहता है। इस साथी ऑब्जेक्ट को अज्ञात विशेषता वस्तु को परिभाषित करने की आवश्यकता है (::)।

val o = DBHelper.loadMyEntityFromDB(primaryKey) :: MyTrait 
o.doSomething 

मैं: अपने उदाहरण कोड के लिए

import MyTrait._ 

val a = new Test 
val b = a :: MyTrait 
b.doSomething 
b.f 

:

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

आप नीचे के रूप में इस विशेषता का उपयोग कर सकते हैं:

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.isInstanceOf [स्ट्रिंग 'जोड़कर यह अच्छी तरह से देख सकते हैं। ] + "/" + m.isInstanceOf [अन्य जल]] '' मुख्य 'समारोह के अंत में। – Hbf

+0

@ axel22 अगर मैं इस तरीके से सही ढंग से समझता हूं तो आप कुछ उदाहरणों में व्यवहार के साथ एक विशेषता (जिसमें कुछ डीफ हैं) मिश्रण कर सकते हैं। लेकिन एक विशेषता में मिश्रण करने में सक्षम नहीं है जिसमें कुछ मूल्य भी हैं, है ना? –

5

मैं आमतौर पर एक मौजूदा ऑब्जेक्ट में एक नई विधि में मिश्रण करने के लिए 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 
} 

और फिर आप पहले से मौजूद किसी वस्तु टेस्ट के साथ 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

एक अंतर्निहित वर्ग के बारे में क्या? अंतिम आंतरिक वर्ग और "मिश्रण" -फंक्शन के साथ अन्य उत्तरों के रास्ते की तुलना में मेरे लिए यह आसान लगता है।

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

अच्छा है लेकिन आपके समाधान के साथ, लक्षण केवल नए और दायरे के साथ बनाए गए उदाहरणों से जुड़ा जा सकता है। कुछ बार आप कहीं और बनाए गए ऑब्जेक्ट पर ट्राइट संलग्न करना चाहते हैं उदा। एक ओआरएम परत से –

संबंधित मुद्दे