2010-07-15 12 views
39

मैं प्राप्त करने के लिएस्कैला में गतिशील मिश्रण - क्या यह संभव है?

def dynamix[A, B](a: A): A with B 

एक उचित कार्यान्वयन मैं जानता हूँ कि हो सकता है क्या बी रहा है करना चाहते हैं क्या है, लेकिन नहीं पता है कि एक है (लेकिन मैं कुछ जोड़ सकता है अगर B एक स्वयं टाइप तो है ए पर बाधाएं)। स्कैला कंपाइलर उपर्युक्त हस्ताक्षर से खुश है, लेकिन मैं अभी तक यह नहीं समझ पाया कि कार्यान्वयन कैसा दिखता है - यदि यह संभव है।

कुछ विकल्प है कि मेरे दिमाग में आया था:

  • प्रतिबिंब/गतिशील प्रॉक्सी का उपयोग करना।
    • सरल मामला: ए जावा स्तर पर एक इंटरफ़ेस है + मैं बी को तत्काल कर सकता हूं और इसका कोई प्रकार नहीं है। मुझे लगता है कि यह बहुत कठिन नहीं होगा (जब तक कि मैं कुछ ग़लत, अप्रत्याशित समस्याओं में भाग नहीं लेता):
      एक नया बी (बी) बनाएं, और ए और बी दोनों को कार्यान्वित करने वाली प्रॉक्सी और एक या बी या तो एक आमंत्रण हैंडलर का उपयोग करके ।
    • यदि बी को तत्काल नहीं किया जा सकता है, तो भी मैं इसका उप-वर्ग बना सकता हूं, और जैसा ऊपर वर्णित किया गया है वैसा ही करें। यदि इसका एक स्व प्रकार भी है तो मुझे शायद यहां और वहां कुछ प्रतिनिधिमंडल की आवश्यकता होगी, लेकिन यह अभी भी काम कर सकता है।
    • लेकिन यदि ए एक ठोस प्रकार है और मुझे इसके लिए उचित इंटरफ़ेस नहीं मिल रहा है तो क्या होगा?
    • क्या मैं और अधिक समस्याओं में भाग लेगा (उदाहरण के लिए रैखिकरण से संबंधित कुछ, या जावा इंटरऑपरेबिलिटी की मदद करने वाली विशेष संरचनाएं)?
  • मिश्रण के बजाय एक प्रकार की रैपिंग का उपयोग करना और बी [ए] को वापस करना, बी से सुलभ है।
    दुर्भाग्य से इस मामले में कॉलर को यह जानने की आवश्यकता होगी कि घोंसले कैसे किया जाता है, जो मिश्रण/लपेटने में कई बार किया जाता है (डी [सी [बी [ए]]]) क्योंकि यह आवश्यक होगा आवश्यक कार्यक्षमता तक पहुंचने के लिए घोंसले का सही स्तर पाएं, इसलिए मैं इसे समाधान नहीं मानता।
  • एक कंपाइलर प्लगइन कार्यान्वित करना। मुझे इसके साथ शून्य अनुभव है लेकिन मेरी आंत महसूस यह है कि यह मामूली नहीं होगा। मुझे लगता है कि केविन राइट का autoproxy प्लगइन थोड़ा सा लक्ष्य है, लेकिन यह मेरी समस्या (अभी तक?) के लिए पर्याप्त नहीं होगा।

क्या आपके पास कोई अन्य विचार है जो काम कर सकता है? आप किस तरह से सिफारिश करेंगे? किस तरह की "चुनौतियों" की उम्मीद है?
या मुझे इसे भूलना चाहिए, क्योंकि मौजूदा स्कैला बाधाओं के साथ यह संभव नहीं है?

मेरी समस्या के पीछे इरादा: कहें कि मेरे पास एक व्यवसाय वर्कफ़्लो है, लेकिन यह बहुत सख्त नहीं है। कुछ चरणों में आदेश तय किया गया है, लेकिन अन्य नहीं करते हैं, लेकिन अंत में उन सभी को किया जाना चाहिए (या उनमें से कुछ को आगे की प्रक्रिया के लिए आवश्यक है)।
थोड़ा और ठोस उदाहरण: मेरे पास ए है, मैं इसमें बी और सी जोड़ सकता हूं। मुझे परवाह नहीं है कि पहले क्या किया जाता है, लेकिन अंत में मुझे सी के साथ बी के साथ ए की आवश्यकता होगी।

टिप्पणी: मुझे ग्रोवी के बारे में बहुत कुछ पता नहीं है लेकिन SO this question पर पॉप अप किया गया और मुझे लगता है कि यह और भी है या कम से कम संकल्पनात्मक, जैसा मैं चाहता हूं उतना ही कम।

+3

एक और दृष्टिकोण (पर्याप्त से नहीं सोचा जानना चाहते हैं कि व्यवहार्य): में एक 'डायनामिक्स लपेटें [ए, बी]' (या नेस्टेड 'डायनेमिक्स [ए, डायनेमिक्स [बी, सी]] ') और क्लाइंट कोड को संज्ञानात्मक रूप से अनलोड करने के लिए प्रत्यारोपण के माध्यम से अनचाहे करें। प्रत्येक घोंसले की गहराई को परिभाषित किया जाना चाहिए (कोई मनमाना गहराई नहीं)। डाउनसाइड: आत्म-प्रकार की बाधाओं को लागू नहीं करेगा या स्वयं प्रकार के माध्यम से प्रकारों पर बातचीत की अनुमति नहीं देगा। अच्छी चाल के लिए –

+0

+1, लेकिन मुझे यह महसूस हो रहा है कि मैं अभी भी कम से कम एक समस्या में चलाऊंगा: अंत में यह निर्दिष्ट करने के लिए कि मैं बी के साथ बी या डायनेमिक्स [ए, डायनेमिक्स [बी, सी] ] लेकिन मुझे मिश्रण आदेश की परवाह नहीं है? मैं डायनेमिक्स [ए, डायनेमिक्स [सी, बी]] के साथ भी खुश रहूंगा, और मुझे लगता है कि इस तरह की बाधा के साथ आने में बहुत आसान नहीं होगा (विशेष रूप से मिश्रित होने के कई गुण हैं)। फिर भी, मुझे आपकी टिप्पणी पसंद है, इसके लिए धन्यवाद। –

+0

संभावित डुप्लिकेट [गतिशील रूप से एक विशेषता में मिलाकर] (http://stackoverflow.com/questions/10373318/mixing-in-a-trait- गतिशील रूप से) –

उत्तर

25

मेरा मानना ​​है कि रनटाइम पर सख्ती से करना असंभव है, क्योंकि नई जावा कक्षाओं में संकलन समय पर लक्षण मिश्रित होते हैं।यदि आप एक मौजूदा वर्ग के साथ एक विशेषता मिला देते हैं तो गुमनाम रूप से आप देख सकते हैं, classfiles देख रही है और javap का उपयोग कर, कि एक गुमनाम, नाम-घायल वर्ग scalac द्वारा बनाई गई है:

class Foo { 
    def bar = 5 
} 

trait Spam { 
    def eggs = 10 
} 

object Main { 
    def main(args: Array[String]) = { 
    println((new Foo with Spam).eggs) 
    } 
} 

scalac Mixin.scala; ls *.class रिटर्न

Foo.class Main$.class Spam$class.class Main$$anon$1.class Main.class Spam.class

जबकि javap Main\$\$anon\$1 रिटर्न

Compiled from "mixin.scala" 

public final class Main$$anon$1 extends Foo implements Spam{ 
    public int eggs(); 
    public Main$$anon$1(); 
} 

आप देख सकते हैं, scalac एक नई अज्ञात वर्ग बनाता है जो रनटाइम पर लोड होता है; संभवतः इस अज्ञात वर्ग में eggs विधि Spam$class का उदाहरण बनाती है और eggs पर कॉल करती है, लेकिन मुझे पूरी तरह से यकीन नहीं है।

import scala.tools.nsc._; 
import scala.reflect.Manifest 

object DynamicClassLoader { 
    private var id = 0 
    def uniqueId = synchronized { id += 1; "Klass" + id.toString } 
} 

class DynamicClassLoader extends 
    java.lang.ClassLoader(getClass.getClassLoader) { 
    def buildClass[T, V](implicit t: Manifest[T], v: Manifest[V]) = { 

    // Create a unique ID 
    val id = DynamicClassLoader.uniqueId 

    // what's the Scala code we need to generate this class? 
    val classDef = "class %s extends %s with %s". 
     format(id, t.toString, v.toString) 

    println(classDef) 

    // fire up a new Scala interpreter/compiler 
    val settings = new Settings(null) 
    val interpreter = new Interpreter(settings) 

    // define this class 
    interpreter.compileAndSaveRun("<anon>", classDef) 

    // get the bytecode for this new class 
    val bytes = interpreter.classLoader.getBytesForClass(id) 

    // define the bytecode using this classloader; cast it to what we expect 
    defineClass(id, bytes, 0, bytes.length).asInstanceOf[Class[T with V]] 
    } 

} 


val loader = new DynamicClassLoader 

val instance = loader.buildClass[Foo, Spam].newInstance 
instance.bar 
// Int = 5 
instance.eggs 
// Int = 10 

जब से तुम जरूरत स्काला संकलक उपयोग करने के लिए, AFAIK, यह शायद साफ समाधान आप प्राप्त करने के लिए कर सकता है के करीब है:

हालांकि, हम यहाँ एक बहुत hacky चाल कर सकते हैं इस। यह काफी धीमी है, लेकिन ज्ञापन शायद काफी मदद करेगा।

यह दृष्टिकोण बहुत हास्यास्पद, हैकी है, और भाषा के अनाज के खिलाफ चला जाता है। मुझे कल्पना है कि अजीब बग के सभी प्रकार रेंग सकते हैं; जिन लोगों ने जावा से अधिक समय तक उपयोग किया है, वे पागलपन के साथ गड़बड़ी के साथ पागलपन की चेतावनी देते हैं।

+2

मैं नुकसान के बारे में पूरी तरह से सहमत हूं, खासतौर से उन संदर्भों में जहां क्लासलोडिंग छोटा नहीं है और/या खेलने के लिए सक्षम है।ईमानदार होने के लिए मैंने एक क्लीनर समाधान की उम्मीद की, लेकिन मुझे यह भी यकीन नहीं था कि यह मौजूद है या नहीं। फिर भी कोई बेहतर जवाब नहीं था और यह शायद काम => स्वीकार किया जाएगा। इसके लिए बहुत धन्यवाद। –

3

मैं अपने स्प्रिंग आवेदन संदर्भ में स्काला सेम का निर्माण करने में सक्षम होना चाहता था, लेकिन मैं यह भी निर्माण किया सेम में शामिल किया जाना mixins निर्दिष्ट करने के लिए सक्षम होने के लिए करना चाहता था:

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:scala="http://www.springframework.org/schema/scala" 
    xsi:schemaLocation=...> 

    <scala:bean class="org.cakesolutions.scala.services.UserService" > 
    <scala:with trait="org.cakesolutions.scala.services.Mixin1" /> 
    <scala:with trait="org.cakesolutions.scala.services.Mixin2" /> 

    <scala:property name="dependency" value="Injected" /> 
    <scala:bean> 
</beans> 

कठिनाई यह है कि कक्षा है .forName फ़ंक्शन मुझे मिश्रित निर्दिष्ट करने की अनुमति नहीं देता है। अंत में, मैंने उपरोक्त हैकी समाधान को स्कैला 2.9.1 तक बढ़ा दिया। तो, यहां यह पूरी तरह से है; वसंत के बिट्स सहित।

class ScalaBeanFactory(private val beanType: Class[_ <: AnyRef], 
         private val mixinTypes: Seq[Class[_ <: AnyRef]]) { 
    val loader = new DynamicClassLoader 
    val clazz = loader.buildClass(beanType, mixinTypes) 

    def getTypedObject[T] = getObject.asInstanceOf[T] 

    def getObject = { 
    clazz.newInstance() 
    } 

    def getObjectType = null 
    def isSingleton = true 

object DynamicClassLoader { 
    private var id = 0 
    def uniqueId = synchronized { id += 1; "Klass" + id.toString } 
} 

class DynamicClassLoader extends java.lang.ClassLoader(getClass.getClassLoader) { 

    def buildClass(t: Class[_ <: AnyRef], vs: Seq[Class[_ <: AnyRef]]) = { 
    val id = DynamicClassLoader.uniqueId 

    val classDef = new StringBuilder 

    classDef.append("class ").append(id) 
    classDef.append(" extends ").append(t.getCanonicalName) 
    vs.foreach(c => classDef.append(" with %s".format(c.getCanonicalName))) 

    val settings = new Settings(null) 
    settings.usejavacp.value = true 
    val interpreter = new IMain(settings) 


    interpreter.compileString(classDef.toString()) 


    val r = interpreter.classLoader.getResourceAsStream(id) 
    val o = new ByteArrayOutputStream 
    val b = new Array[Byte](16384) 
    Stream.continually(r.read(b)).takeWhile(_ > 0).foreach(o.write(b, 0, _)) 
    val bytes = o.toByteArray 

    defineClass(id, bytes, 0, bytes.length) 
    } 

} 

कोड अभी तक मानकों के साथ कंस्ट्रक्टर के साथ सौदा नहीं कर सकते हैं और माता पिता के वर्ग के निर्माता (यह है कि क्या करना चाहिए?) से एनोटेशन नकल नहीं करता है। हालांकि, यह हमें एक अच्छा प्रारंभिक बिंदु देता है जो स्काल स्प्रिंग नेमस्पेस में उपयोग योग्य है। बेशक, अभी इसके लिए मेरे शब्द नहीं लेते, एक Specs2 विनिर्देश में यह सत्यापित करें:

class ScalaBeanFactorySpec extends Specification { 

    "getTypedObject mixes-in the specified traits" in { 
    val f1 = new ScalaBeanFactory(classOf[Cat], 
            Seq(classOf[Speaking], classOf[Eating])) 

    val c1 = f1.getTypedObject[Cat with Eating with Speaking] 

    c1.isInstanceOf[Cat with Eating with Speaking] must_==(true) 

    c1.speak // in trait Speaking 
    c1.eat  // in trait Eating 
    c1.meow  // in class Cat 
    } 

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