क्लोन विधि को ठीक से ओवरराइड कैसे करें?


114

मुझे अपनी एक ऐसी वस्तु में एक गहरे क्लोन को लागू करने की आवश्यकता है जिसमें कोई सुपरक्लास नहीं है।

CloneNotSupportedExceptionसुपरक्लास (जो है Object) द्वारा फेंके गए चेक को संभालने का सबसे अच्छा तरीका क्या है ?

एक सहकर्मी ने मुझे इसे निम्नलिखित तरीके से संभालने की सलाह दी:

@Override
public MyObject clone()
{
    MyObject foo;
    try
    {
        foo = (MyObject) super.clone();
    }
    catch (CloneNotSupportedException e)
    {
        throw new Error();
    }

    // Deep clone member fields here

    return foo;
}

यह मेरे लिए एक अच्छा समाधान की तरह लगता है, लेकिन मैं इसे स्टैकऑवरफ्लो समुदाय के लिए बाहर फेंकना चाहता था यह देखने के लिए कि क्या कोई अन्य अंतर्दृष्टि है जिसे मैं शामिल कर सकता हूं। धन्यवाद!


6
यदि आप जानते हैं कि मूल वर्ग लागू करता है, Cloneableतो AssertionErrorकेवल एक मैदान के बजाय फेंकना Errorथोड़ा अधिक अभिव्यंजक है।
एंड्रयू डफी

संपादित करें: मैं क्लोन () का उपयोग नहीं करूंगा, लेकिन परियोजना पहले से ही इस पर आधारित है और यह इस बिंदु पर सभी संदर्भों को फिर से बनाने के लायक नहीं होगा।
14

बुलेट को काटने के लिए बेहतर है बजाय बाद में (अच्छी तरह से, जब तक कि आप उत्पादन हिट करने के बारे में न हों)।
टॉम हॉल्टिन -

जब तक यह पूरा नहीं हो जाता तब तक हमारे पास शेड्यूल पर डेढ़ महीना बचा है। हम समय को उचित नहीं ठहरा सकते।
14:25 बजे कोगा

मुझे लगता है कि यह समाधान एकदम सही है। और क्लोन का उपयोग करने के बारे में बुरा मत मानना। यदि आपके पास एक वर्ग है जो एक परिवहन वस्तु के रूप में उपयोग किया जाता है और इसमें केवल प्राइमिटव्स और अपरिवर्तनीयताएं होती हैं जैसे स्ट्रिंग और एनम, क्लोन के अलावा बाकी सब कुछ हठधर्मी कारणों के लिए समय की पूरी बर्बादी होगी। बस ध्यान रखें कि क्लोन क्या करता है और क्या नहीं! (कोई गहरी प्रतिरूपण)
टॉमवॉक

जवाबों:


125

क्या आपको पूरी तरह से उपयोग करना है clone? ज्यादातर लोग सहमत हैं कि जावा cloneटूट गया है।

डिजाइन पर जोश बलोच - कॉपी कंस्ट्रक्टर बनाम क्लोनिंग

यदि आपने मेरी पुस्तक में क्लोनिंग के बारे में आइटम पढ़ा है, खासकर यदि आप लाइनों के बीच पढ़ते हैं, तो आपको पता चल जाएगा कि मुझे लगता cloneहै कि गहराई से टूट गया है। [...] यह एक शर्म की बात है Cloneableजो टूट गया है, लेकिन ऐसा होता है।

आप उनकी पुस्तक प्रभावी जावा 2 संस्करणclone में विषय पर अधिक चर्चा पढ़ सकते हैं , आइटम 11: विवेकपूर्ण रूप से ओवरराइड करें । वह कॉपी कंस्ट्रक्टर या कॉपी फैक्ट्री का उपयोग करने के बजाय सिफारिश करता है।

उन्होंने कहा कि यदि आपको लगता है कि आप को लागू करना चाहिए, तो आप पृष्ठों के पन्नों को लिखेंगे clone। लेकिन वह इस के साथ बंद हो गया:

क्या यह सब जटिलताएँ वास्तव में आवश्यक हैं? शायद ही कभी। यदि आप एक ऐसे वर्ग का विस्तार करते हैं जो लागू करता है Cloneable, तो आपके पास एक अच्छा व्यवहार cloneविधि लागू करने के अलावा बहुत कम विकल्प हैं । अन्यथा, आप ऑब्जेक्ट कॉपी करने के वैकल्पिक साधन प्रदान करने, या बस क्षमता प्रदान नहीं करने से बेहतर हैं

जोर उसका था, मेरा नहीं।


चूंकि आपने यह स्पष्ट कर दिया है कि आपके पास इसे लागू करने के अलावा और कोई विकल्प नहीं है clone, इसलिए इस मामले में आप क्या कर सकते हैं: यह सुनिश्चित करें MyObject extends java.lang.Object implements java.lang.Cloneable। अगर ऐसा है, तो आप गारंटी दे सकते हैं कि आप कभी भी पकड़ नहीं पाएंगे CloneNotSupportedExceptionAssertionErrorजैसा कि कुछ ने सुझाव दिया है फेंकना उचित प्रतीत होता है, लेकिन आप एक टिप्पणी भी जोड़ सकते हैं जो बताती है कि इस विशेष मामले में कैच ब्लॉक को कभी दर्ज क्यों नहीं किया जाएगा ।


वैकल्पिक रूप से, जैसा कि दूसरों ने भी सुझाव दिया है, आप शायद cloneबिना बुलाए लागू कर सकते हैं super.clone


1
दुर्भाग्य से, इस परियोजना को पहले से ही क्लोन विधि का उपयोग करते हुए लिखा गया है, अन्यथा मैं बिल्कुल इसका उपयोग नहीं करता। मैं आपसे पूरी तरह सहमत हूं कि जावा के क्लोन का कार्यान्वयन फ़ाकाटा है।
कोगा

5
यदि कोई वर्ग और उसके सभी सुपरक्लास super.clone()अपने क्लोन तरीकों के भीतर कॉल करते हैं, तो एक उप-वर्ग को आम तौर पर केवल तभी ओवरराइड करना होगा clone()यदि यह नए फ़ील्ड जोड़ता है जिसकी सामग्री को क्लोन करने की आवश्यकता होगी। यदि कोई सुपरक्लास newइसके बजाय उपयोग करता है super.clone(), तो सभी उपवर्गों को ओवरराइड करना चाहिए clone()चाहे वे किसी नए क्षेत्र को जोड़ते हों या नहीं।
सुपरकैट

56

कभी-कभी कॉपी कंस्ट्रक्टर को लागू करना अधिक सरल होता है:

public MyObject (MyObject toClone) {
}

यह आपको हैंडलिंग की परेशानी से बचाता है CloneNotSupportedException, finalखेतों के साथ काम करता है और आपको लौटने के प्रकार के बारे में चिंता करने की ज़रूरत नहीं है।


11

जिस तरह से आपका कोड काम करता है, उसे लिखने के लिए "कैनोनिकल" तरीके से बहुत करीब है। मैं AssertionErrorहालांकि पकड़ के भीतर फेंक देंगे । यह संकेत देता है कि उस रेखा तक कभी नहीं पहुंचना चाहिए।

catch (CloneNotSupportedException e) {
    throw new AssertionError(e);
}

यह गलत विचार क्यों हो सकता है, इसके लिए @polygenel स्नेहक का उत्तर देखें।
कार्ल रिक्टर

@KarlRichter मैंने उनका जवाब पढ़ा है। यह नहीं कहता कि यह एक बुरा विचार है, इसके अलावा Cloneableसामान्य रूप से एक टूटा हुआ विचार है, जैसा कि प्रभावी जावा में समझाया गया है। ओपी ने पहले ही व्यक्त कर दिया कि उन्हें उपयोग करना है Cloneable। इसलिए मुझे इस बात का कोई अंदाजा नहीं है कि मेरे जवाब को और बेहतर कैसे बनाया जा सकता है, सिवाय इसके कि शायद इसे एकमुश्त हटा दिया जाए।
क्रिस जस्टर-यंग

9

ऐसे दो मामले हैं, जिनमें CloneNotSupportedExceptionफेंक दिया जाएगा:

  1. क्लोन किया जा रहा वर्ग लागू नहीं होता है Cloneable(यह मानते हुए कि वास्तविक क्लोनिंग अंततः Object'क्लोन पद्धति' की ओर झुकती है )। यदि आप जिस कक्षा में यह विधि लागू कर रहे हैं Cloneable, वह कभी नहीं होगी (क्योंकि कोई भी उप-वर्ग इसे उचित रूप से विरासत में प्राप्त करेगा)।
  2. अपवाद को स्पष्ट रूप से एक कार्यान्वयन द्वारा फेंक दिया जाता है - सुपरक्लास होने पर उपवर्ग में क्लोनिंग को रोकने के लिए यह अनुशंसित तरीका है Cloneable

उत्तरार्द्ध मामला आपकी कक्षा में नहीं हो सकता है (जैसा कि आप सीधे tryब्लॉक में सुपरक्लास पद्धति को बुला रहे हैं , भले ही एक उपवर्ग कॉल से आह्वान किया गया हो super.clone()) और पूर्व को तब तक नहीं होना चाहिए जब तक कि आपकी कक्षा स्पष्ट रूप से लागू नहीं होनी चाहिए Cloneable

मूल रूप से, आपको त्रुटि सुनिश्चित करने के लिए लॉग इन करना चाहिए, लेकिन इस विशेष उदाहरण में यह तभी होगा जब आप अपनी कक्षा की परिभाषा को गड़बड़ करेंगे। इस प्रकार इसे NullPointerException(या समान) के चेक किए गए संस्करण की तरह व्यवहार करें - यदि आपका कोड कार्यशील है तो इसे कभी नहीं फेंका जाएगा।


अन्य स्थितियों में आपको इस घटना के लिए तैयार रहने की आवश्यकता होगी - इस बात की कोई गारंटी नहीं है कि किसी दिए गए ऑब्जेक्ट को क्लोन किया जा सकता है, इसलिए अपवाद को पकड़ते समय आपको इस स्थिति के आधार पर उचित कार्रवाई करनी चाहिए (मौजूदा ऑब्जेक्ट के साथ जारी रखें, एक वैकल्पिक क्लोनिंग रणनीति लें जैसे क्रमबद्ध- IllegalParameterExceptionडिसेरिएलाइज़, यदि आपके विधि को क्लोन करने योग्य, आदि आदि द्वारा पैरामीटर की आवश्यकता होती है, तो फेंक दें )।

संपादित करें : हालांकि कुल मिलाकर मुझे यह इंगित करना चाहिए कि हां, clone()वास्तव में सही ढंग से लागू करना मुश्किल है और कॉल करने वालों के लिए यह जानना मुश्किल है कि क्या वापसी मूल्य वे चाहते हैं, दोगुना होगा जब आप गहरे बनाम उथले क्लोनों पर विचार करते हैं। यह पूरी तरह से पूरी तरह से बचने और किसी अन्य तंत्र का उपयोग करने के लिए अक्सर बेहतर होता है।


यदि कोई ऑब्जेक्ट सार्वजनिक क्लोनिंग विधि को उजागर करता है, तो कोई भी व्युत्पन्न वस्तु जो इसका समर्थन नहीं करती है, वह लिस्कोव प्रतिस्थापन सिद्धांत का उल्लंघन करेगी। यदि एक क्लोनिंग विधि संरक्षित है, तो मुझे लगता है कि यह उचित प्रकार को वापस करने की विधि के अलावा किसी अन्य चीज़ के साथ छाया करना बेहतर होगा, एक उपवर्ग को भी कॉल करने की कोशिश करने से रोकने के लिए super.clone()
सुपरकैट

5

गहरी प्रतियां बनाने के लिए क्रमांकन का उपयोग करें । यह सबसे तेज समाधान नहीं है लेकिन यह प्रकार पर निर्भर नहीं करता है।


यह एक महान और स्पष्ट समाधान था, इस पर विचार करते हुए कि मैं पहले से ही GSON का उपयोग कर रहा था।
जैकब तबक

3

आप संरक्षित कॉपी कंस्ट्रक्टर को इस तरह लागू कर सकते हैं:

/* This is a protected copy constructor for exclusive use by .clone() */
protected MyObject(MyObject that) {
    this.myFirstMember = that.getMyFirstMember(); //To clone primitive data
    this.mySecondMember = that.getMySecondMember().clone(); //To clone complex objects
    // etc
}

public MyObject clone() {
    return new MyObject(this);
}

3
यह ज्यादातर समय काम नहीं करता है। जब तक इसका कोई तरीका न हो, तब तक आप clone()लौटाए गए ऑब्जेक्ट पर कॉल नहीं कर सकते । getMySecondMember()public clone
पॉलीजेनिल लुब्रिकेंट्स

जाहिर है, आपको इस पैटर्न को हर उस वस्तु पर लागू करने की ज़रूरत है, जिसे आप क्लोन करना चाहते हैं, या प्रत्येक सदस्य को गहरी क्लोन करने का एक और तरीका खोज सकते हैं।
डॉल्फ

हाँ, यह एक आवश्यक आवश्यकता है।
पॉलीजेन लुब्रिकेंट्स

1

यहाँ जितने भी उत्तर दिए गए हैं, उनमें से अधिकांश उत्तर मान्य हैं, मुझे यह बताने की आवश्यकता है कि आपका समाधान यह भी है कि वास्तविक जावा एपीआई डेवलपर्स कैसे करते हैं। (या तो जोश बलोच या नील ने बाद में)

यहाँ openJDK, ArrayList वर्ग से एक उद्धरण है:

public Object clone() {
    try {
        ArrayList<?> v = (ArrayList<?>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

जैसा कि आपने देखा है और दूसरों ने उल्लेख किया है, CloneNotSupportedExceptionफेंकने का लगभग कोई मौका नहीं है यदि आपने घोषित किया है कि आप Cloneableइंटरफ़ेस को लागू करते हैं ।

इसके अलावा, यदि आपको ओवरराइड विधि में कुछ नया नहीं करना है तो आपको इस विधि को ओवरराइड करने की कोई आवश्यकता नहीं है। आपको केवल इसे ओवरराइड करने की आवश्यकता है जब आपको ऑब्जेक्ट पर अतिरिक्त संचालन करने की आवश्यकता होती है या आपको इसे सार्वजनिक करने की आवश्यकता होती है।

अंततः, इससे बचना और किसी अन्य तरीके का उपयोग करना अभी भी सबसे अच्छा है।


0
public class MyObject implements Cloneable, Serializable{   

    @Override
    @SuppressWarnings(value = "unchecked")
    protected MyObject clone(){
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {
            ByteArrayOutputStream bOs = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bOs);
            oos.writeObject(this);
            ois = new ObjectInputStream(new ByteArrayInputStream(bOs.toByteArray()));
            return  (MyObject)ois.readObject();

        } catch (Exception e) {
            //Some seriouse error :< //
            return null;
        }finally {
            if (oos != null)
                try {
                    oos.close();
                } catch (IOException e) {

                }
            if (ois != null)
                try {
                    ois.close();
                } catch (IOException e) {

                }
        }
    }
}

इसलिए आपने इसे केवल फ़ाइल सिस्टम पर लिखा है और ऑब्जेक्ट को वापस पढ़ा है। ठीक है, क्या क्लोन को संभालने का यह सबसे अच्छा तरीका है? क्या एसओ समुदाय का कोई भी व्यक्ति इस दृष्टिकोण पर टिप्पणी कर सकता है? मुझे लगता है कि यह अनावश्यक रूप से क्लोनिंग और सीरियललाइज़ेशन को जोड़ता है - दो पूरी तरह से अलग अवधारणाएं। मैं यह देखने के लिए इंतजार करूंगा कि दूसरों का इस बारे में क्या कहना है।
सौरभ पाटिल

2
इसकी फाइल सिस्टम में नहीं है, यह मुख्य मेमोरी में है (बाइटएर्रेऑटपुटस्ट्रीम)। गहरी नेस्टेड ऑब्जेक्ट्स के लिए इसका समाधान thats अच्छी तरह से काम करता है। खासकर यदि आपको यूनिट टेस्ट में अक्सर इसकी आवश्यकता नहीं होती है, जैसे। जहां प्रदर्शन मुख्य लक्ष्य नहीं है।
एलेक्सविन

0

सिर्फ इसलिए कि जावा के क्लोएनेबल का कार्यान्वयन टूट गया है इसका मतलब यह नहीं है कि आप अपना खुद का एक निर्माण नहीं कर सकते।

यदि ओपी वास्तविक उद्देश्य एक गहरी क्लोन बनाने के लिए था, तो मुझे लगता है कि इस तरह एक इंटरफ़ेस बनाना संभव है:

public interface Cloneable<T> {
    public T getClone();
}

फिर इसे लागू करने से पहले उल्लिखित प्रोटोटाइप निर्माता का उपयोग करें:

public class AClass implements Cloneable<AClass> {
    private int value;
    public AClass(int value) {
        this.vaue = value;
    }

    protected AClass(AClass p) {
        this(p.getValue());
    }

    public int getValue() {
        return value;
    }

    public AClass getClone() {
         return new AClass(this);
    }
}

और AClass वस्तु क्षेत्र के साथ एक और वर्ग:

public class BClass implements Cloneable<BClass> {
    private int value;
    private AClass a;

    public BClass(int value, AClass a) {
         this.value = value;
         this.a = a;
    }

    protected BClass(BClass p) {
        this(p.getValue(), p.getA().getClone());
    }

    public int getValue() {
        return value;
    }

    public AClass getA() {
        return a;
    }

    public BClass getClone() {
         return new BClass(this);
    }
}

इस तरह आप आसानी से @SuppressWarnings या अन्य बनावटी कोड की आवश्यकता के बिना वर्ग BClass की एक वस्तु को गहरा क्लोन कर सकते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.