क्यों सुपर है। जावा में अनुमति नहीं है?


360

मैं इस सवाल को पढ़ता हूं और सोचा जाता है कि आसानी से हल हो जाएगा (ऐसा नहीं है कि यह बिना हल नहीं है) अगर कोई लिख सकता है:

@Override
public String toString() {
    return super.super.toString();
}

मुझे यकीन नहीं है कि यह कई मामलों में उपयोगी है, लेकिन मुझे आश्चर्य है कि ऐसा क्यों नहीं है और अगर ऐसा कुछ अन्य भाषाओं में मौजूद है।

आप लोग क्या सोचते हैं?

संपादित करें: स्पष्ट करने के लिए: हाँ मुझे पता है, यह जावा में असंभव है और मैं वास्तव में इसे याद नहीं करता हूं। यह कुछ भी नहीं है जो मुझे काम करने की उम्मीद थी और एक संकलक त्रुटि हो रही थी। मुझे सिर्फ विचार करना था और इस पर चर्चा करना पसंद था।


7
कॉल करने के लिए चाहते हैं super.super.toString()अपने खुद के निर्णय के विपरीत है जब आप एक वर्ग इस प्रकार स्वीकार करने बढ़ाने का चयन सभी (नहीं से कुछ) अपनी सुविधाओं।
दिनमान

जवाबों:


484

यह इनकैप्सुलेशन का उल्लंघन करता है। आपको मूल वर्ग के व्यवहार को बायपास करने में सक्षम नहीं होना चाहिए। यह समझ में आता है कि कभी-कभी आपकी अपनी कक्षा के व्यवहार (विशेषकर उसी पद्धति से) को दरकिनार किया जा सकता है, लेकिन आपके माता-पिता का नहीं। उदाहरण के लिए, मान लें कि हमारे पास आधार "वस्तुओं का संग्रह" है, "लाल वस्तुओं का संग्रह" का प्रतिनिधित्व करने वाला एक उपवर्ग और "बड़े लाल वस्तुओं का संग्रह" का प्रतिनिधित्व करने वाला उपवर्ग। यह समझ में आता है:

public class Items
{
    public void add(Item item) { ... }
}

public class RedItems extends Items
{
    @Override
    public void add(Item item)
    {
        if (!item.isRed())
        {
            throw new NotRedItemException();
        }
        super.add(item);
    }
}

public class BigRedItems extends RedItems
{
    @Override
    public void add(Item item)
    {
        if (!item.isBig())
        {
            throw new NotBigItemException();
        }
        super.add(item);
    }
}

यह ठीक है - RedItems हमेशा आश्वस्त रह सकता है कि इसमें शामिल आइटम सभी लाल हैं। अब मान लें कि हम सुपर.सुपर कॉल करने में सक्षम थे। ():

public class NaughtyItems extends RedItems
{
    @Override
    public void add(Item item)
    {
        // I don't care if it's red or not. Take that, RedItems!
        super.super.add(item);
    }
}

अब हम जो चाहें जोड़ सकते हैं, और इनवॉयरेंट RedItemsटूट गया है।

क्या इसका कोई मतलब है?


38
अच्छा उदाहरण है। लेकिन मुझे लगा कि जब बेस क्लास आइटम स्वीकार करता है, तो यह खराब डिजाइन है, लेकिन व्युत्पन्न वर्ग उन्हें अस्वीकार कर देता है, क्योंकि बेस क्लास (प्रतिस्थापन सिद्धांत का उल्लंघन) के लिए व्युत्पन्न को ड्रॉप-इन प्रतिस्थापन के रूप में इस्तेमाल नहीं किया जा सकता है। क्या यह सही सोच है, या ऐसा पदानुक्रम साफ है?
जोहान्स स्काउब -

5
क्या आप विरासत से अधिक रचना का मतलब है? यह उदाहरण वंशानुक्रम से बचने का एक कारण नहीं है - हालाँकि बहुत सारे हैं।
जॉन स्केट

3
@ समय: यह केवल एक आसान-से-समझने वाला उदाहरण है जो उल्लंघन का उल्लंघन करता है। इसकी जगह कोई प्रॉपर्टी सेट कर सकता है। इसके अलावा, सभी पहलू एक प्रकार के स्तर पर दिखाई नहीं देंगे। जेनरिक हर चीज का जवाब नहीं है।
जॉन स्कीट

12
@ जोहान्सचैब-लिटब मुझे लगता है कि यह लिस्कोव प्रतिस्थापन सिद्धांत का उल्लंघन करता है, क्योंकि यदि कोई आइटम के अनुबंध में कोड करता है और RedItems के उदाहरण का उपयोग करता है, तो किसी को अप्रत्याशित NotRedItemException मिलेगा। मुझे हमेशा सिखाया जाता था कि एक उप वर्ग को इनपुट का एक सुपर सेट लेना चाहिए और आउटपुट का सबसेट वापस करना चाहिए। दूसरे शब्दों में, एक उप वर्ग को कभी भी ऐसे इनपुट को अस्वीकार नहीं करना चाहिए जो सुपर क्लास के लिए मान्य हो या आउटपुट जो कि सुपर क्लास उत्पन्न नहीं कर सकता है, इसमें एक त्रुटि शामिल है जिसे सुपर क्लास फेंक नहीं देता है।
कोनस्टैंटिन ताराशांस्कियां

4
@piechuckerr: कभी-कभी किताबें, कभी-कभी ब्लॉग पोस्ट, कभी-कभी सिर्फ अनुभव ...
जॉन स्केट

72

मुझे लगता है कि जॉन स्कीट का सही जवाब है। मैं यह जोड़ना चाहूंगा कि आप कास्टिंग द्वारा सुपरक्लासेस के छायांकित चर का उपयोग कर सकते हैंthis :

interface I { int x = 0; }
class T1 implements I { int x = 1; }
class T2 extends T1 { int x = 2; }
class T3 extends T2 {
        int x = 3;
        void test() {
                System.out.println("x=\t\t"          + x);
                System.out.println("super.x=\t\t"    + super.x);
                System.out.println("((T2)this).x=\t" + ((T2)this).x);
                System.out.println("((T1)this).x=\t" + ((T1)this).x);
                System.out.println("((I)this).x=\t"  + ((I)this).x);
        }
}

class Test {
        public static void main(String[] args) {
                new T3().test();
        }
}

जो उत्पादन का उत्पादन करता है:

x = 3
सुपर .x = 2
((T2) यह) .x = 2
((T1) यह) .x = 1
((I) यह) .x = 0

( जेएलएस से उदाहरण )

हालाँकि, यह विधि कॉल के लिए काम नहीं करता है क्योंकि विधि कॉल ऑब्जेक्ट के रनटाइम प्रकार के आधार पर निर्धारित की जाती है।


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

अभिव्यक्ति दस्तावेज़ के लिए बढ़िया लिंक। OCPJP के लिए अध्ययन करने वाले लोगों के लिए कुछ अच्छे उदाहरण।
गॉर्डन

बहुत बढ़िया। मैंने कभी किसी कास्ट का इस्तेमाल करने के बारे में नहीं सोचा होगा।
थॉमस एडिंग

कैसे आता है कक्षा T1 ओवरराइड चर x? डिफ़ॉल्ट रूप से इसका स्थिर फाइनल?
अमरनाथ ने

@amarnathharish: यह ओवरराइड नहीं है, और फ़ील्ड डिफ़ॉल्ट रूप से पैकेज-संरक्षित गैर-अंतिम हैं।
माइकल मायर्स

41

मुझे लगता है कि निम्नलिखित कोड सुपर का उपयोग करने की अनुमति देते हैं। सुपर ... अधिकांश मामलों में सुपर.method ()। (भले ही ऐसा करने के लिए ugly है)

संक्षेप में

  1. पूर्वज प्रकार के अस्थायी उदाहरण बनाएँ
  2. मूल ऑब्जेक्ट से अस्थायी एक तक फ़ील्ड्स की प्रतिलिपि बनाएँ
  3. अस्थायी ऑब्जेक्ट पर लक्ष्य विधि लागू करें
  4. मूल ऑब्जेक्ट पर वापस संशोधित मान कॉपी करें

उपयोग:

public class A {
   public void doThat() { ... }
}

public class B extends A {
   public void doThat() { /* don't call super.doThat() */ }
}

public class C extends B {
   public void doThat() {
      Magic.exec(A.class, this, "doThat");
   }
}


public class Magic {
    public static <Type, ChieldType extends Type> void exec(Class<Type> oneSuperType, ChieldType instance,
            String methodOfParentToExec) {
        try {
            Type type = oneSuperType.newInstance();
            shareVars(oneSuperType, instance, type);
            oneSuperType.getMethod(methodOfParentToExec).invoke(type);
            shareVars(oneSuperType, type, instance);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    private static <Type, SourceType extends Type, TargetType extends Type> void shareVars(Class<Type> clazz,
            SourceType source, TargetType target) throws IllegalArgumentException, IllegalAccessException {
        Class<?> loop = clazz;
        do {
            for (Field f : loop.getDeclaredFields()) {
                if (!f.isAccessible()) {
                    f.setAccessible(true);
                }
                f.set(target, f.get(source));
            }
            loop = loop.getSuperclass();
        } while (loop != Object.class);
    }
}

10
परावर्तन के साथ आप कुछ भी कर सकते हैं, हाँ :) आप तार को परिवर्तनशील भी बना सकते हैं।
बालुसक

23
क्या ही भयानक बात है! मैं आपको केवल यह पता लगाने के लिए +1 दे रहा हूं कि यह कैसे करना है :)
लैरी वातानाबे

6
यह एक अच्छी चाल है, लेकिन यह भी हमेशा के लिए कॉल करने के बराबर नहीं है, अभी तक की जरूरत है) सुपर। सुपर और इसलिए है कि सुपर। सुपर कॉल सी (सी + बी + ए) के संदर्भ में ले जाएगा, जबकि आपके जवाब एक उदाहरण बनाता है B और C. के संदर्भ के बिना A तो यह उत्तर काम नहीं करेगा यदि प्रत्येक doThat को getContext () कहा जाता है, उदाहरण के लिए, और getContext को प्रत्येक कक्षा में अलग-अलग तरीके से लागू किया गया था। आपके उत्तर में यह A के getContext () का उपयोग करेगा जबकि अनुपलब्ध सुपर.सुपर को कॉल करने के परिणामस्वरूप C के getContext का उपयोग किया जाएगा।
Inor

हम्म। कुछ मामलों में, शायद आप डायनेमिक प्रॉक्सिस ( javahowto.blogspot.co.uk/2011/12/… ) के साथ inor की आपत्ति को दूर कर सकते हैं , मूल ऑब्जेक्ट पर कॉल को रीडायरेक्ट करना (प्रत्येक कॉल के बाद वेरिएबल्स को सिंक्रनाइज़ करना)? ऐसा लगता है कि परदे के पीछे एक इंटरफ़ेस को लागू करने के लिए सब कुछ की आवश्यकता होती है, हालांकि। इसके अलावा, मुझे आश्चर्य है कि अगर सुपर-सुपर क्लास के लिए अपने सुपर-सुपर तरीकों में से एक को कॉल करना संभव है, विशेष रूप से, और आपको उन लोगों को पुनर्निर्देशित करने की आवश्यकता नहीं होगी ...
एरहानिस

मैंने एक और टिप्पणी करते हुए कहा कि super.super.प्रोग्रामर को एक नया हल खोजने के लिए प्रोग्रामर को आमंत्रित करने के लिए आमंत्रित करता है, जो एक प्रभावी तरीके से अपने आप को पैर में गोली मारता है, यह इसका एक आदर्श उदाहरण है, क्योंकि आपके सहकर्मी शायद आपसे कुछ लिखने के लिए बहुत नफरत करेंगे। इस तरह कि वे व्यक्तिगत रूप से और सचमुच आपको पैर में गोली मार देंगे। +1
ब्रैडेन बेस्ट

11

मेरे पास टिप्पणी करने के लिए पर्याप्त प्रतिष्ठा नहीं है इसलिए मैं इसे अन्य उत्तरों में जोड़ दूंगा।

जॉन स्कीट एक सुंदर उदाहरण के साथ, उत्कृष्ट रूप से जवाब देता है। मैट बी में एक बिंदु है: सभी सुपरक्लासेस में सुपरर्स नहीं हैं। यदि आप एक सुपर के सुपर कहते हैं तो आपका कोड टूट जाएगा, जिसमें कोई सुपर नहीं था।

ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग (जो जावा है) ऑब्जेक्ट्स के बारे में है, फ़ंक्शंस नहीं। यदि आप कार्य उन्मुख प्रोग्रामिंग चाहते हैं, तो C ++ या कुछ और चुनें। यदि आपकी वस्तु सुपर क्लास में फिट नहीं होती है, तो आपको इसे "दादा दादी वर्ग" में जोड़ने की जरूरत है, एक नया वर्ग बनाएं, या एक और सुपर खोजें जो इसमें फिट हो।

व्यक्तिगत रूप से, मैंने जावा की सबसे बड़ी ताकत में से एक होने के लिए इस सीमा को पाया है। कोड कुछ अन्य भाषाओं की तुलना में कठोर है, जो मैंने उपयोग किया है, लेकिन मुझे हमेशा पता है कि क्या उम्मीद है। यह जावा के "सरल और परिचित" लक्ष्य के साथ मदद करता है। मेरे दिमाग में सुपर.सुपर कॉलिंग सरल या परिचित नहीं है। शायद डेवलपर्स ने ऐसा ही महसूस किया?


3
आप कहते हैं "सभी सुपरक्लास में सुपरर्स नहीं होते हैं"। खैर, सभी लेकिन java.lang.Object आपको "अशक्त" क्या दे सकता है। तो मैं कहूंगा, लगभग सभी के पास सुपरर्स हैं।
टिम ब्यूटे

हर प्रोग्राम जो एप्लिकेशन प्रोग्रामर लिखता है उसमें एक "सुपर" होता है (java.lang.Object नहीं करता है, लेकिन एप्लिकेशन प्रोग्राम ऐसा नहीं लिखता है।)
फाइनेंन जूल

2
अगर बहुत सारे सुपरर्स हैं तो सुपर.सुपर.सुपर ... सुपर कंपाइल टाइम एरर बनाकर आसानी से हल किया जा सकता है। जावा को ध्यान में रखते हुए केवल सार्वजनिक विरासत है, अगर किसी ने विरासत पदानुक्रम को बदल दिया है, तो आप इंटरफ़ेस बदल रहे हैं। इसलिए मुझे इस बात की चिंता नहीं होगी कि सुपर ^ एन डरावना है।
थॉमस एडिंग

7

ऐसा करने के लिए कुछ अच्छे कारण हैं। आपके पास एक उपवर्ग हो सकता है जिसमें एक विधि है जिसे गलत तरीके से लागू किया गया है, लेकिन मूल विधि सही तरीके से लागू की गई है। क्योंकि यह तीसरे पक्ष के पुस्तकालय से संबंधित है, आप स्रोत को बदलने में असमर्थ / अनिच्छुक हो सकते हैं। इस स्थिति में, आप एक उपवर्ग बनाना चाहते हैं, लेकिन सुपर.सुपर विधि को कॉल करने के लिए एक विधि को ओवरराइड करते हैं।

जैसा कि कुछ अन्य पोस्टरों द्वारा दिखाया गया है, प्रतिबिंब के माध्यम से ऐसा करना संभव है, लेकिन कुछ ऐसा करना संभव होना चाहिए

(SuperSuperClass इसे) .Method ();

मैं अभी इस समस्या से निपट रहा हूं - त्वरित समाधान सुपरक्लास पद्धति को उप-वर्ग विधि में कॉपी और पेस्ट करना है :)


1
किसी विधि को कॉल करने से पहले कास्टिंग करने से उस पद्धति को परिवर्तित नहीं किया जाता है, जिसे यह निर्दिष्ट किया जाता है - यह हमेशा उपवर्ग का कार्यान्वयन है जिसका उपयोग किया जाता है, कभी भी सुपर नहीं। देखें, उदाहरण के लिए, stackoverflow.com/questions/1677993/…
जोशुआ गोल्डबर्ग

1
@ यह पूरी तरह से स्थिति मैं में था, और वास्तव में मैं इस्तेमाल किया तय है। अच्छा निर्णय!
bcr

यदि आपके पास आवश्यक विधि का कोड है, तो आप सभी आवश्यक फ़ील्ड खोल सकते हैं और इस कोड को अपने उपवर्ग में चला सकते हैं।
एनबीबी

6

दूसरों द्वारा किए गए बहुत अच्छे बिंदुओं के अलावा, मुझे लगता है कि एक और कारण है: क्या होगा यदि सुपरक्लास में सुपरक्लास नहीं है?

चूंकि प्रत्येक वर्ग स्वाभाविक रूप से (कम से कम) फैलता है Object, super.whatever()हमेशा सुपरक्लास में एक विधि का उल्लेख करेगा। लेकिन क्या होगा यदि आपकी कक्षा केवल फैली हुई है Object- super.superतब क्या संदर्भित होगा ? उस व्यवहार को कैसे संभाला जाना चाहिए - एक संकलक त्रुटि, एक नलपॉइंट, आदि।

मुझे लगता है कि प्राथमिक कारण यह अनुमति नहीं है कि यह इनकैप्सुलेशन का उल्लंघन करता है, लेकिन यह एक छोटा कारण भी हो सकता है।


4
जाहिर है, यह एक संकलक त्रुटि होगी - और यह जानकारी है कि संकलक के पास क्या है।
माइकल बोर्गवर्ड

4

मुझे लगता है कि यदि आप एक विधि को अधिलेखित करते हैं और इसके सभी सुपर-क्लास संस्करण चाहते हैं (जैसे, के लिए कहते हैं equals ), तो आप वास्तव में हमेशा सीधे सुपरक्लास संस्करण को कॉल करना चाहते हैं, जो कि बदले में इसके सुपरक्लास संस्करण को कॉल करेगा ।

मुझे लगता है कि यह केवल शायद ही कभी समझ में आता है (अगर बिल्कुल भी। मैं एक ऐसे मामले के बारे में नहीं सोच सकता हूं) जहां एक विधि के कुछ मनमाने सुपरक्लास को कॉल किया जा सके। मुझे नहीं पता कि जावा में यह संभव है या नहीं। यह C ++ में किया जा सकता है:

this->ReallyTheBase::foo();

6
यह समझ में आता है अगर किसी ने गलत तरीके से लागू की गई एक विधि के साथ एक उपवर्ग लिखा है, लेकिन सुपरक्लास विधि 90% काम करती है। फिर आप एक उपवर्ग बनाना चाहते हैं, और सुपरक्लास सुपरक्लास पद्धति को कॉल करने की विधि को ओवरराइड करें, और अपना 10% जोड़ें।
लैरी वातानाबे

3

एक अनुमान में, क्योंकि इसका उपयोग अक्सर नहीं किया जाता है। इसका उपयोग करने का एकमात्र कारण मैं यह देख सकता हूं कि यदि आपके प्रत्यक्ष माता-पिता ने कुछ कार्यक्षमता को ओवरराइड किया है और आप इसे मूल में वापस लाने का प्रयास कर रहे हैं।

जो मुझे OO सिद्धांतों के खिलाफ लगता है, क्योंकि कक्षा के प्रत्यक्ष माता-पिता को आपकी कक्षा से अधिक निकटता से संबंधित होना चाहिए, जैसा कि दादा-दादी है।


3

देखो इस Github परियोजना, विशेष रूप से objectHandle चर। इस परियोजना से पता चलता है कि वास्तव में पोते पर दादा-दादी की विधि को कैसे और सही तरीके से कहा जाता है।

बस लिंक टूट जाने की स्थिति में, यहाँ कोड है:

import lombok.val;
import org.junit.Assert;
import org.junit.Test;

import java.lang.invoke.*;

/*
Your scientists were so preoccupied with whether or not they could, they didn’t stop to think if they should.
Please don't actually do this... :P
*/
public class ImplLookupTest {
    private MethodHandles.Lookup getImplLookup() throws NoSuchFieldException, IllegalAccessException {
        val field = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
        field.setAccessible(true);
        return (MethodHandles.Lookup) field.get(null);
    }

    @Test
    public void test() throws Throwable {
        val lookup = getImplLookup();
        val baseHandle = lookup.findSpecial(Base.class, "toString",
            MethodType.methodType(String.class),
            Sub.class);
        val objectHandle = lookup.findSpecial(Object.class, "toString",
            MethodType.methodType(String.class),
            // Must use Base.class here for this reference to call Object's toString
            Base.class);
        val sub = new Sub();
        Assert.assertEquals("Sub", sub.toString());
        Assert.assertEquals("Base", baseHandle.invoke(sub));
        Assert.assertEquals(toString(sub), objectHandle.invoke(sub));
    }

    private static String toString(Object o) {
        return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode());
    }

    public class Sub extends Base {
        @Override
        public String toString() {
            return "Sub";
        }
    }

    public class Base {
        @Override
        public String toString() {
            return "Base";
        }
    }
}

हैप्पी कोडिंग !!!!


आपके वैज्ञानिक इस बात से पहले से ही चिंतित थे कि वे कर सकते हैं या नहीं, वे सोचना बंद नहीं करते थे कि क्या करना चाहिए। कृपया वास्तव में ऐसा न करें ...: P 🤔
टिम ब्यूटे

हाँ, वे कोडर शब्द हैं, मेरा नहीं। मेरी राय में, मुझे लगता है कि आपको वास्तव में किसी दिन इसकी आवश्यकता हो सकती है
kyay

2

यदि संभव हो तो मैं सुपर.सुपर विधि निकाय को दूसरी विधि में रखूंगा

class SuperSuperClass {
    public String toString() {
        return DescribeMe();
    }

    protected String DescribeMe() {
        return "I am super super";
    }
}

class SuperClass extends SuperSuperClass {
    public String toString() {
        return "I am super";
    }
}

class ChildClass extends SuperClass {
    public String toString() {
        return DescribeMe();
    }
}

या यदि आप सुपर-सुपर क्लास को नहीं बदल सकते हैं, तो आप यह कोशिश कर सकते हैं:

class SuperSuperClass {
    public String toString() {
        return "I am super super";
    }
}

class SuperClass extends SuperSuperClass {
    public String toString() {
        return DescribeMe(super.toString());
    }

    protected String DescribeMe(string fromSuper) {
        return "I am super";
    }
}

class ChildClass extends SuperClass {
    protected String DescribeMe(string fromSuper) {
        return fromSuper;
    }
}

दोनों ही मामलों में,

new ChildClass().toString();

"मैं सुपर सुपर हूं" के परिणाम


मैंने अभी खुद को ऐसी स्थिति में पाया है जहां मैं सुपरसुपरक्लास और चाइल्डक्लास का मालिक हूं, लेकिन सुपरक्लास नहीं, इसलिए मैंने पहला समाधान उपयोगी पाया।
ज़ोफ़न

1

यह कम से कम सुपरक्लास के सुपरक्लास के वर्ग को प्राप्त करना संभव प्रतीत होगा, हालांकि जरूरी नहीं कि इसका उदाहरण, प्रतिबिंब का उपयोग करके; यदि यह उपयोगी हो सकता है, तो कृपया जावदोक पर विचार करें http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Class.html#getSuperclass ()


1
public class A {

     @Override
     public String toString() {
          return "A";
     }

}


public class B extends A {

     @Override
     public String toString() {
          return "B";
     }

}

public class C extends B {

     @Override
     public String toString() {
          return "C";
     }

}


public class D extends C {

     @Override
     public String toString() {
          String result = "";
          try {
                result = this.getClass().getSuperclass().getSuperclass().getSuperclass().newInstance().toString();
          } catch (InstantiationException ex) {
                Logger.getLogger(D.class.getName()).log(Level.SEVERE, null, ex);
          } catch (IllegalAccessException ex) {
                Logger.getLogger(D.class.getName()).log(Level.SEVERE, null, ex);
          }
          return result;
     }

}

public class Main {

     public static void main(String... args) {
          D d = new D();
          System.out.println(d);

     }
}

रन: ए ब्यूल्ड SUCCESSFUL (कुल समय: 0 सेकंड)


1
मैं देखता हूं, लेकिन आप एक नया उदाहरण बना रहे हैं, तो यह काम नहीं करेगा यदि ऑब्जेक्ट में कोई स्थिति है।
टिम ब्यूथ

1

जब आप आधार वर्ग का कोड नहीं बदल सकते हैं तब सुपर.सुपरमिथोड () का कॉल करना समझ में आता है। यह अक्सर तब होता है जब आप किसी मौजूदा लाइब्रेरी का विस्तार कर रहे होते हैं।

पहले खुद से पूछिए, आप उस क्लास को क्यों बढ़ा रहे हैं? यदि उत्तर "क्योंकि मैं इसे बदल नहीं सकता" तो आप अपने आवेदन में सटीक पैकेज और वर्ग बना सकते हैं, और शरारती विधि या प्रतिनिधि को फिर से लिख सकते हैं:

package com.company.application;

public class OneYouWantExtend extends OneThatContainsDesiredMethod {

    // one way is to rewrite method() to call super.method() only or 
    // to doStuff() and then call super.method()

    public void method() {
        if (isDoStuff()) {
            // do stuff
        }
        super.method();
    }

    protected abstract boolean isDoStuff();


    // second way is to define methodDelegate() that will call hidden super.method()

    public void methodDelegate() {
        super.method();
    }
    ...
}

public class OneThatContainsDesiredMethod {

    public void method() {...}
    ...
}

उदाहरण के लिए, आप अपने आवेदन में org.springframework.test.context.junit4.SpringJUnit4ClassRunner वर्ग बना सकते हैं, इसलिए इस वर्ग को जार से वास्तविक एक से पहले लोड किया जाना चाहिए। फिर तरीकों या निर्माणकर्ताओं को फिर से लिखना।

ध्यान दें: यह पूर्ण हैक है, और इसका उपयोग करने के लिए अत्यधिक अनुशंसित नहीं है, लेकिन यह काम कर रहा है! क्लास लोडर के साथ संभावित मुद्दों के कारण इस दृष्टिकोण का उपयोग खतरनाक है। साथ ही यह उन समस्याओं का कारण बन सकता है जब आप लाइब्रेरी को अपडेट करेंगे जिसमें अधिलेखित वर्ग शामिल है।


1

मेरे पास इस तरह की स्थितियां हैं जब वास्तुकला को एक सामान्य CustomBaseClass में सामान्य कार्यक्षमता का निर्माण करना है जो कई व्युत्पन्न वर्गों की ओर से लागू होता है। हालांकि, हमें विशिष्ट व्युत्पन्न वर्ग के लिए विशिष्ट विधि के लिए सामान्य तर्क को दरकिनार करने की आवश्यकता है। ऐसे मामलों में, हमें एक सुपर.सुपर.methodX कार्यान्वयन का उपयोग करना चाहिए।

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

        ...
        FrameworkBaseClass (....) extends...
        {
           methodA(...){...}
           methodB(...){...}
        ...
           methodX(...)
        ...
           methodN(...){...}

        }
        /* CustomBaseClass overrides default framework functionality for benefit of several derived classes.*/
        CustomBaseClass(...) extends FrameworkBaseClass 
        {
        private boolean skipMethodX=false; 
        /* implement accessors isSkipMethodX() and setSkipMethodX(boolean)*/

           methodA(...){...}
           methodB(...){...}
        ...
           methodN(...){...}

           methodX(...){
                  if (isSkipMethodX()) {
                       setSKipMethodX(false);
                       super.methodX(...);
                       return;
                       }
                   ... //common method logic
            }
        }

        DerivedClass1(...) extends CustomBaseClass
        DerivedClass2(...) extends CustomBaseClass 
        ...
        DerivedClassN(...) extends CustomBaseClass...

        DerivedClassX(...) extends CustomBaseClass...
        {
           methodX(...){
                  super.setSKipMethodX(true);
                  super.methodX(...);
                       }
        }

हालाँकि, आर्किटेक्चर के साथ-साथ ऐप में अच्छे आर्किटेक्चर सिद्धांतों का पालन किया गया है, हम इसा दृष्टिकोण के बजाय हैस एप्रोच का उपयोग करके ऐसी स्थितियों से आसानी से बच सकते हैं। लेकिन हर समय जगह में अच्छी तरह से डिज़ाइन किए गए आर्किटेक्चर की अपेक्षा करना बहुत व्यावहारिक नहीं है, और इसलिए ठोस डिजाइन सिद्धांतों से दूर होने और इस तरह से हैक लगाने की आवश्यकता है। बस मेरे 2 सेंट ...


1

@ जीन स्कीट अच्छी व्याख्या। IMO यदि कोई सुपर.सुपर विधि को कॉल करना चाहता है, तो किसी को तत्काल माता-पिता के व्यवहार को अनदेखा करना चाहिए, लेकिन भव्य माता-पिता के व्यवहार को एक्सेस करना चाहते हैं। यह उदाहरण के माध्यम से प्राप्त किया जा सकता है। नीचे दिए गए कोड के रूप में

public class A {
    protected void printClass() {
        System.out.println("In A Class");
    }
}

public class B extends A {

    @Override
    protected void printClass() {
        if (!(this instanceof C)) {
            System.out.println("In B Class");
        }
        super.printClass();
    }
}

public class C extends B {
    @Override
    protected void printClass() {
        System.out.println("In C Class");
        super.printClass();
    }
}

यहाँ ड्राइवर वर्ग है,

public class Driver {
    public static void main(String[] args) {
        C c = new C();
        c.printClass();
    }
}

इसका आउटपुट होगा

In C Class
In A Class

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


1
खैर, यह रचनात्मक है लेकिन वास्तव में मेरे सवाल का जवाब नहीं देता है। C अभी भी सुपर कॉल नहीं करता है। सुपर, B सिर्फ अलग तरह से व्यवहार करता है। यदि आप ए और बी को बदल सकते हैं तो आप इंस्टोफ़ का उपयोग करने के बजाय सिर्फ एक और तरीका जोड़ सकते हैं। Super.super.foo उन मामलों में आपकी मदद करेगा जहां आपके पास ए और बी तक पहुंच नहीं है और उन्हें बदल नहीं सकते।
टिम बुथ

@TimButhe से सहमत हों, लेकिन यदि कोई सुपर.सुपर पर कॉल करना चाहता है तो वह जानबूझकर पेरेंट क्लास के व्यवहार को नजरअंदाज करना चाहता है, इसलिए यू सिर्फ जावा के मौजूदा सिंटैक्स द्वारा उस चीज को हासिल करने की आवश्यकता है। (आप जो भी विकल्प चाहते हैं, वह या तो अलग है / अलग विधि से)
संजय जैन

0

यदि आपको लगता है कि आपको सुपरक्लास की आवश्यकता होने वाली है, तो आप इसे उस वर्ग के लिए एक चर में संदर्भित कर सकते हैं। उदाहरण के लिए:

public class Foo
{
  public int getNumber()
  {
    return 0;
  }
}

public class SuperFoo extends Foo
{
  public static Foo superClass = new Foo();
  public int getNumber()
  {
    return 1;
  }
}

public class UltraFoo extends Foo
{
  public static void main(String[] args)
  {
    System.out.println(new UltraFoo.getNumber());
    System.out.println(new SuperFoo().getNumber());
    System.out.println(new SuperFoo().superClass.getNumber());
  }
  public int getNumber()
  {
    return 2;
  }
}

प्रिंट आउट लेना चाहिए:

2
1
0

2
आपका बहिर्गमन तरह का है ... अच्छा बुरा है, क्योंकि आप स्थैतिक तरीकों का उपयोग करते हैं। स्थैतिक तरीकों का उपयोग करते समय, आपको चर या सुपर की बिल्कुल भी आवश्यकता नहीं होती है। हो सकता है कि आपने कुछ बुनियादी OO अवधारणा को यहाँ याद किया हो, सुनिश्चित करें कि आप उस पर पढ़ें। नीचे करना है, क्षमा करें।
टिम बुथ

1
यह आसानी से स्थिर तरीकों के बिना किया जा सकता था। यह काफी सरल है। मैं यह कैसे करना है पर एक सरल उदाहरण के लिए जा रहा था।
Ashtheking

मुझे आपकी बात समझ में आ गई है, सुपर चर को किसी क्षेत्र में संग्रहीत करना इसका समाधान करने का एक तरीका है लेकिन, आपको चर की जरूरत नहीं है अगर यह एक स्थिर विधि है, तो आप इसका उपयोग कर सकते हैं। दूसरे, स्थिर तरीकों को लागू करना एक चर एक बुरा अभ्यास है और अधिकांश IDE आपको इसके बारे में चेतावनी देंगे। यदि आप अपने उत्तर को ठीक करते हैं, तो स्थैतिक सामान को हटाकर, मुझे अपने डाउनवोट को हटाने में खुशी होगी, कोई अपराध नहीं।
टिम बुथ

आप पास हैं, लेकिन आपका कोड संकलित नहीं होगा। आप अपने मुख्य विधि में एक स्थिर संदर्भ से getNumber तक पहुंचने का प्रयास करें। क्या आपने वास्तव में इसे संकलित करने की कोशिश की है? (और अपने अल्ट्राफू को सुपरफू का विस्तार नहीं करना चाहिए?)
टिम ब्यूथ

मैं वास्तव में आपके साथ क्रूर नहीं होना चाहता, लेकिन new UltraFoo.getNumber()संकलन नहीं करूंगा, क्योंकि आप कोष्ठक से चूक गए थे। हालाँकि, मैंने अभी अपना डोनवोट हटा दिया, क्योंकि आपके कोड की अवधारणा अब बहुत स्पष्ट है, धन्यवाद!
टिम ब्यूथ

0

IMO, super.super.sayYourName()जावा में व्यवहार को प्राप्त करने का एक अच्छा तरीका है ।

public class GrandMa {  
    public void sayYourName(){  
        System.out.println("Grandma Fedora");  
    }  
}  

public class Mama extends GrandMa {  
    public void sayYourName(boolean lie){  
        if(lie){   
            super.sayYourName();  
        }else {  
            System.out.println("Mama Stephanida");  
        }  
    }  
}  

public class Daughter extends Mama {  
    public void sayYourName(boolean lie){  
        if(lie){   
            super.sayYourName(lie);  
        }else {  
            System.out.println("Little girl Masha");  
        }  
    }  
}  

public class TestDaughter {
    public static void main(String[] args){
        Daughter d = new Daughter();

        System.out.print("Request to lie: d.sayYourName(true) returns ");
        d.sayYourName(true);
        System.out.print("Request not to lie: d.sayYourName(false) returns ");
        d.sayYourName(false);
    }
}

आउटपुट:

Request to lie: d.sayYourName(true) returns Grandma Fedora
Request not to lie: d.sayYourName(false) returns Little girl Masha


आह, तो आप इस तरह एक वर्ग पदानुक्रम को लागू करने की वकालत कर रहे हैं? दुर्भाग्य से, यह शुरू होता है वास्तव में प्राप्त करने के लिए गंदा आप बेबी (बेटी की उपवर्ग) से माँ में विधि का उपयोग करना चाहते हैं तो ...
बीसीआर

याकॉव फेन सही है। दूसरे शब्दों में, यह एक अच्छा उदाहरण नहीं है, क्योंकि मूल प्रश्न सुपर.सुपर में एक ओवरराइड विधि को कॉल करने के बारे में था।
Inor

0

मुझे लगता है कि यह एक समस्या है जो विरासत के समझौते को तोड़ती है।
एक वर्ग का विस्तार करने से आप उसके व्यवहार को मानते / स्वीकार करते हैं,
जब भी बुलाते हैं super.super.method(), आप अपनी आज्ञाकारिता समझौते को तोड़ना चाहते हैं।

आप सिर्फ सुपर क्लास से चेरी नहीं ले सकते

हालांकि, ऐसी स्थितियां हो सकती हैं जब आपको कॉल करने की आवश्यकता महसूस होती है super.super.method()- आमतौर पर एक बुरा डिज़ाइन साइन, आपके कोड में या आपके द्वारा विरासत में दिए गए कोड में!
यदि सुपर और सुपर सुपर कक्षाएं (कुछ विरासत कोड) को फिर से सक्रिय नहीं किया जा सकता है, तो विरासत पर रचना का विकल्प चुनें।

Encapsulation तोड़ने जब आप है @Override समझाया कोड तोड़कर कुछ तरीकों। ओवरराइड नहीं किए जाने वाले तरीकों को अंतिम रूप से चिह्नित किया गया है ।


0

C # में आप किसी भी पूर्वज की विधि को इस तरह कह सकते हैं:

public class A
    internal virtual void foo()
...
public class B : A
    public new void foo()
...
public class C : B
    public new void foo() {
       (this as A).foo();
    }

आप डेल्फी में भी ऐसा कर सकते हैं:

type
   A=class
      procedure foo;
      ...
   B=class(A)
     procedure foo; override;
     ...
   C=class(B)
     procedure foo; override;
     ...
A(objC).foo();

लेकिन जावा में आप केवल कुछ गियर द्वारा ही इस तरह का फोकस कर सकते हैं। एक संभावित तरीका है:

class A {               
   int y=10;            

   void foo(Class X) throws Exception {  
      if(X!=A.class)
         throw new Exception("Incorrect parameter of "+this.getClass().getName()+".foo("+X.getName()+")");
      y++;
      System.out.printf("A.foo(%s): y=%d\n",X.getName(),y);
   }
   void foo() throws Exception { 
      System.out.printf("A.foo()\n");
      this.foo(this.getClass()); 
   }
}

class B extends A {     
   int y=20;            

   @Override
   void foo(Class X) throws Exception { 
      if(X==B.class) { 
         y++; 
         System.out.printf("B.foo(%s): y=%d\n",X.getName(),y);
      } else { 
         System.out.printf("B.foo(%s) calls B.super.foo(%s)\n",X.getName(),X.getName());
         super.foo(X);
      } 
   }
}

class C extends B {     
   int y=30;            

   @Override
   void foo(Class X) throws Exception { 
      if(X==C.class) { 
         y++; 
         System.out.printf("C.foo(%s): y=%d\n",X.getName(),y);
      } else { 
         System.out.printf("C.foo(%s) calls C.super.foo(%s)\n",X.getName(),X.getName());
         super.foo(X);
      } 
   }

   void DoIt() {
      try {
         System.out.printf("DoIt: foo():\n");
         foo();         
         Show();

         System.out.printf("DoIt: foo(B):\n");
         foo(B.class);  
         Show();

         System.out.printf("DoIt: foo(A):\n");
         foo(A.class);  
         Show();
      } catch(Exception e) {
         //...
      }
   }

   void Show() {
      System.out.printf("Show: A.y=%d, B.y=%d, C.y=%d\n\n", ((A)this).y, ((B)this).y, ((C)this).y);
   }
} 

objC.DoIt () परिणाम आउटपुट:

DoIt: foo():
A.foo()
C.foo(C): y=31
Show: A.y=10, B.y=20, C.y=31

DoIt: foo(B):
C.foo(B) calls C.super.foo(B)
B.foo(B): y=21
Show: A.y=10, B.y=21, C.y=31

DoIt: foo(A):
C.foo(A) calls C.super.foo(A)
B.foo(A) calls B.super.foo(A)
A.foo(A): y=11
Show: A.y=11, B.y=21, C.y=31

C # में यह केवल गैर-आभासी तरीकों के लिए काम करेगा और चूंकि सभी तरीके जावा में आभासी हैं, इसलिए यह वास्तव में अलग नहीं है।
Agent_L

0

यह करना आसान है। उदाहरण के लिए:

C, B का B उपवर्ग और A का उपवर्ग। तीनों में विधि विधि नाम () उदाहरण के लिए है।

public abstract class A {

    public void methodName() {
        System.out.println("Class A");
    }

}

public class B extends A {

    public void methodName() {
        super.methodName();
        System.out.println("Class B");
    }

    // Will call the super methodName
    public void hackSuper() {
        super.methodName();
    }

}

public class C extends B {

    public static void main(String[] args) {
        A a = new C();
        a.methodName();
    }

    @Override
    public void methodName() {
        /*super.methodName();*/
        hackSuper();
        System.out.println("Class C");
    }

}

रन क्लास सी आउटपुट होगा: क्लास ए क्लास सी

आउटपुट के बजाय: क्लास ए क्लास बी क्लास सी


-1
public class SubSubClass extends SubClass {

    @Override
    public void print() {
        super.superPrint();
    }

    public static void main(String[] args) {
        new SubSubClass().print();
    }
}

class SuperClass {

    public void print() {
        System.out.println("Printed in the GrandDad");
    }
}

class SubClass extends SuperClass {

    public void superPrint() {
        super.print();
    }
}

आउटपुट: ग्रैंडडैड में मुद्रित


2
यह उत्तर प्रश्न के दायरे से बाहर है। ओपी ने यह नहीं पूछा कि दादा-दादी कक्षा में एक विधि कैसे कॉल करें। मुद्दा super.super.method()जावा में मान्य कोड क्यों नहीं है की चर्चा है ।
जेड शफ

-1

कीवर्ड सुपरक्लास में विधि को लागू करने का एक तरीका है। जावा ट्यूटोरियल में: https://docs.oracle.com/javase/tutorial/java/IandI/super.html

यदि आपकी विधि अपने सुपरक्लास के तरीकों में से एक को ओवरराइड करती है, तो आप कीवर्ड सुपर के उपयोग के माध्यम से ओवरराइड विधि को लागू कर सकते हैं।

विश्वास नहीं है कि यह सुपर वस्तु का एक संदर्भ है !!! नहीं, यह सुपरक्लास में तरीकों को लागू करने के लिए एक कीवर्ड है।

यहाँ एक उदाहरण है:

class Animal {
    public void doSth() {
        System.out.println(this);   // It's a Cat! Not an animal!
        System.out.println("Animal do sth.");
    }
}

class Cat extends Animal {
    public void doSth() {
        System.out.println(this);
        System.out.println("Cat do sth.");
        super.doSth();
    }
}

जब आप कॉल करते हैं cat.doSth(), तो doSth()कक्षा में विधि Animalमुद्रित होगी thisऔर यह एक बिल्ली है।

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