2011-11-02 33 views
12

इन दोनों जावा वर्गों लें:दो अपरिवर्तनीय वस्तुओं को बनाने का कोई अच्छा तरीका एक दूसरे को संदर्भित करता है?

class User { 
    final Inventory inventory; 
    User (Inventory inv) { 
     inventory = inv; 
    } 
} 

class Inventory { 
    final User owner; 
    Inventory (User own) { 
     owner = own; 
    } 
} 

वहाँ किसी भी तरह से without using reflection* इस बंद को खींचने के लिए है? मुझे वास्तव में यह उम्मीद नहीं है, लेकिन यह पूछने में कोई दिक्कत नहीं हो सकती है।

अद्यतन: बाईटकोड निर्माण में के बाद से दो चरणों (1. आवंटित वस्तु, 2. कॉल निर्माता **) इस हो सकता है (ab) हस्तलिखित बाईटकोड या एक कस्टम संकलक के साथ, यह करने के लिए इस्तेमाल किया गया है? मैं पहले दोनों ऑब्जेक्ट्स के लिए चरण 1 करने के बारे में बात कर रहा हूं, फिर चरण 1 से संदर्भों का उपयोग करके दोनों के लिए चरण 2। बेशक ऐसा कुछ बोझिल होगा, और सवाल का यह हिस्सा अकादमिक है।

(* क्योंकि प्रतिबिंब एक सुरक्षा प्रबंधक के साथ परेशानी दे सकता है)

(** कहते हैं अपने सीमित ज्ञान)

+1

परिपत्र निर्भरता अक्सर एक अच्छा विचार नहीं है – Bozho

+0

मुझे खेद है, क्या आप समस्या पर थोड़ा सा विस्तार कर सकते हैं। मुझे नहीं पता कि समस्या क्या है, क्योंकि आपके पास पहले से ही अन्य ऑब्जेक्ट का संदर्भ है? – Anders

+0

@ एंडर्स समस्या यह है कि दोनों को निर्माण समय पर दूसरे के संदर्भ की आवश्यकता होती है, और ऐसा संदर्भ निर्माण के बाद ही उपलब्ध है। –

उत्तर

13

यह केवल सफाई से काम कर सकते हैं वस्तुओं में से एक अन्य द्वारा बनाई गई है। अभी तक यह पूरी तरह से आरंभ नहीं कर रहा है: उदाहरण के लिए आप कुछ इस तरह करने के लिए अपने User वर्ग बदल सकते हैं (जबकि Inventory वर्ग अपरिवर्तित रखते हुए):

class User { 
    private final Inventory inventory; 
    User() { 
     inventory = new Inventory(this); 
    } 
} 

आप Inventory निर्माता में User वस्तु तक पहुँचने तथापि, के बारे में सावधान रहने की जरूरत है । उदाहरण के लिए, इसका inventory फ़ील्ड अभी भी null होगा!

विज्ञापन अद्यतन: अब मैं सत्यापित किया है कि बाईटकोड-हेरफेर दृष्टिकोण काम नहीं करता। मैंने Jasmin का उपयोग करके इसे आजमाया है और यह हमेशा VerifyError से लोड करने में विफल रहा है।

इस मुद्दे में गहराई से वितरण, मुझे § 4.10.2.4 Instance Initialization Methods and Newly Created Objects मिला। यह खंड बताता है कि JVM कैसे सुनिश्चित करता है कि केवल प्रारंभिक ऑब्जेक्ट इंस्टेंस पास हो जाएं।

+0

बाइटकोड चाल के बारे में कोई विचार (अद्यतन प्रश्न देखें)? मैं जो वर्णन करता हूं वह शायद असंभव है। –

+0

@ BartvanHeukelom: सैद्धांतिक रूप से यह काम कर सकता है। मुझे नहीं पता कि JVM वास्तव में कन्स्ट्रक्टर को आमंत्रित करने के लिए सही 'invokespecial' के बिना 'नया' पसंद करता है या नहीं। मुझे इसे आज़माएं ... –

+0

1.4 में परिवर्तन हुए थे (जेवीएसपीसी द्वितीय एड के बाद) जिसने कक्षा के बाहर अंतिम क्षेत्रों को प्रारंभ करने के लिए कुछ पहुंच की अनुमति दी। मुझे सटीक परिवर्तन याद नहीं हैं। –

7

यदि आप किसी ऑब्जेक्ट को इंजेक्ट करने की आवश्यकता नहीं है तो आप इसे कर सकते हैं।

class User { 
    private final Inventory inventory; 
    User() { 
     inventory = new Inventory(this); 
    } 
} 
3
class User { 
    private final Inventory inventory; 
    User (/*whatever additional args are needed to construct the inventory*/) { 
     //populate user fields 
     inventory = new Inventory(this); 
    } 
} 

class Inventory { 
    private final User owner; 
    Inventory (User own) { 
     owner = own; 
    } 
} 

कि सबसे अच्छा मैं के बारे में सोच सकते हैं। शायद एक बेहतर पैटर्न है।

0

यह एक "समाधान" है, हालांकि final का नुकसान असुविधाजनक है।

class User { 
    Inventory inventory; 
    User() { } 
    // make sure this setter is only callable from where it should be, 
    // and is called only once at construction time 
    setInventory(inv) { 
     if (inventory != null) throw new IllegalStateException(); 
     inventory = inv; 
    } 
} 

class Inventory { 
    final User owner; 
    Inventory (User own) { 
     owner = own; 
    } 
} 
+1

दाएं। अब आपके पास दो अपरिवर्तनीय वस्तुएं नहीं हैं, जो आपके प्रश्न का शीर्षक आधार के रूप में राज्यों का शीर्षक है;) – aioobe

+0

@aioobe वास्तव में, इसलिए मैंने इसे संपादित किया और उद्धरणों में "समाधान" लगाया। यह अभी भी बाहरी दुनिया के लिए अपरिवर्तनीय है, क्योंकि वहां कोई सेटटर नहीं है जिसका उपयोग वे कर सकते हैं। –

+0

दाएं, जब तक कि आप एक ही पैकेज में अन्य कक्षाओं को बाहरी दुनिया के रूप में नहीं मानते: पी – aioobe

0

आप केवल JVM बाईटकोड में रुचि रखते हैं और विशेष रूप से जावा में कोडिंग, शायद स्काला या Clojure का उपयोग कर मदद कर सकता है के बारे में परवाह नहीं करते हैं। आपको किसी प्रकार की letrec मशीनरी की आवश्यकता होगी।

1

थोड़ा सा पैडेंटिक, लेकिन अगर आप थोड़ा संकेत नहीं देते हैं, तो यह एक दूसरे के अंदर बनाने के लिए सख्ती से जरूरी नहीं है। वे दोनों आंतरिक कक्षाएं हो सकती हैं।

public class BadlyNamedClass { 
    private final User owner; 
    private final Inventory inventory; 

    public BadlyNamedClass() { 
     this.owner = new User() { 
      ... has access to BadlyNamedClass.this.inventory; 
     }; 
     this.inventory = new Inventory() { 
      ... has access to BadlyNamedClass.this.owner; 
     }; 
    } 
    ... 
} 

या भी:

public class BadlyNamedClass { 
    private final User owner; 
    private final Inventory inventory; 

    public BadlyNamedClass() { 
     this.owner = new User(this); 
     this.inventory = new Inventory(this); 
    } 
    public User getOwner() { return owner; } 
    public Inventory getInventory() { return inventory; } 
    ... 
} 
+0

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

+0

अच्छी तरह से सोचा, हालांकि अभी भी एक समस्या है क्योंकि आपकी तीसरी अपरिवर्तनीय वस्तु इसके कन्स्ट्रक्टर में भाग रही है। –

+0

@AlexandreDupriez कौन सा? 'मालिक' और 'सूची' के 'अंतिम' क्षेत्र अर्थशास्त्र, आदि अप्रासंगिक हैं। वे 'BadlyNamedClass' के उन लोगों द्वारा कवर किए गए हैं। ('स्ट्रिंग' जिसे आम तौर पर 'char []' का उपयोग करके कार्यान्वित किया जाता है, इसका कैननिकल उदाहरण है।) –

0

बी: "उपयोगकर्ता द्वारा बनाई गई सूची हमारे पिछले आशा है।"
वाई: "नहीं, एक और है।"

यदि आप किसी तीसरे पक्ष के संदर्भों को सार करते हैं, तो आप उस संबंध में नियंत्रण कर सकते हैं।

उदाहरण के लिए।

public class User 
{ 
    private final String identifier; // uniquely identifies this User instance. 

    public User(final String myIdentifier) 
    { 
     identifier = myIdentifier; 

     InventoryReferencer.registerBlammoUser(identifier); // Register the user with the Inventory referencer. 
    } 

    public Inventory getInventory() 
    { 
     return InventoryReferencer.getInventoryForUser(identifier); 
    } 
} 

public interface Inventory // Bam! 
{ 
    ... nothing special. 
} 

// Assuming that the Inventory only makes sence in the context of a User (i.e. User must own Inventory). 
public class InventoryReferencer 
{ 
    private static final Map<String, Inventory> referenceMap = new HashMap<String, Inventory>(); 

    private InventoryReferencer() 
    { 
     throw ... some exception - helps limit instantiation. 
    } 

    public static void registerBlammoUser(final String identifier) 
    { 
     InventoryBlammo blammo = new InventoryBlammo(); 
     referenceMap.add(indentifier, blammo); 
    } 

    public static void registerKapowUser(final String identifier) 
    { 
     InventoryBlammo kapow = new InventoryKapow(); 
     referenceMap.add(indentifier, kapow); 
    } 

    public static Inentory getInfentoryForUser(final String identifier) 
    { 
     return referenceMap.get(identifier); 
    } 
} 

// Maybe package access constructors. 
public class InventoryBlammo implements Inventory 
{ 
    // a Blammo style inventory. 
} 

public class InventoryKapow implements Inventory 
{ 
    // a Kapow style inventory. 
}