जावा नेम हाइडिंग: द हार्ड वे


96

मुझे नाम छिपाने की समस्या है जिसे हल करना बेहद कठिन है। यहाँ एक सरलीकृत संस्करण है जो समस्या की व्याख्या करता है:

एक वर्ग है: org.A

package org;
public class A{
     public class X{...}
     ...
     protected int net;
}

फिर एक वर्ग है net.foo.X

package net.foo;
public class X{
     public static void doSomething();
}

और अब, यहाँ समस्याग्रस्त वर्ग है जो विरासत में मिला है Aऔर कॉल करना चाहता हैnet.foo.X.doSomething()

package com.bar;
class B extends A {

    public void doSomething(){
        net.foo.X.doSomething(); // doesn't work; package net is hidden by inherited field
        X.doSomething(); // doesn't work; type net.foo.X is hidden by inherited X
    }
}

जैसा कि आप देख रहे हैं, यह संभव नहीं है। मैं सरल नाम का उपयोग नहीं कर सकता Xक्योंकि यह एक विरासत प्रकार द्वारा छिपा हुआ है। मैं पूरी तरह से योग्य नाम का उपयोग नहीं कर सकता net.foo.X, क्योंकि netएक विरासत क्षेत्र द्वारा छिपा हुआ है।

केवल कक्षा Bमेरे कोड आधार में है; कक्षाएं net.foo.Xऔर org.Aपुस्तकालय कक्षाएं हैं, इसलिए मैं उन्हें बदल नहीं सकता!

मेरा एकमात्र समाधान इस तरह दिखता है: मैं एक अन्य वर्ग को कॉल कर सकता था जो बदले में कॉल करता है X.doSomething(); लेकिन यह वर्ग केवल नाम के टकराव के कारण मौजूद होगा, जो बहुत ही गन्दा लगता है! वहाँ कोई समाधान नहीं है, जिसमें मैं सीधे कॉल कर सकते हैं है X.doSomething()से B.doSomething()?

वैश्विक नामस्थान निर्दिष्ट करने की अनुमति देने वाली भाषा में, उदाहरण के लिए, global::C # या ::C ++ में, मैं बस netइस वैश्विक उपसर्ग के साथ उपसर्ग कर सकता हूं , लेकिन जावा इसकी अनुमति नहीं देता है।


कुछ QuickFix इस हो सकता है: public void help(net.foo.X x) { x.doSomething(); }और के साथ फोनhelp(null);
बेतुका-मन

@ बेतुका-मन: ठीक है, एक गैर-मौजूद वस्तु के माध्यम से एक स्थिर विधि को कॉल करना मेरे समाधान से भी अधिक अजीब लगता है :)। मैं भी एक संकलक चेतावनी प्राप्त करेंगे। लेकिन सच है, यह मेरा के अलावा एक और हैकी समाधान होगा।
gexicide

@ जेम्स: यह गलत एक्स को प्रेरित करेगा! net.foo.Xविधि है, नहीं org.A.X!
१६:४४ पर gexicide

3
क्या आप है से प्राप्त करना A? वंशानुक्रम इतना बुरा हो सकता है, जितना आपने पाया है ...
डोनल फेलो

2
I could call another class that in turn calls X.doSomething(); but this class would only exist because of the name clash, which seems very messyस्वच्छ कोड के दृष्टिकोण के लिए +1। लेकिन मेरे लिए ऐसा लगता है कि यह एक ऐसी स्थिति है जिसमें आपको एक व्यापार करना चाहिए। बस ऐसा करें, और एक अच्छी लंबी टिप्पणी फेंकें कि आपको ऐसा क्यों करना पड़ा (शायद इस सवाल के लिंक के साथ)।
सम्पतस्री

जवाबों:


84

आप nullटाइप करने के लिए कास्ट कर सकते हैं और फिर उस पर विधि लागू कर सकते हैं (जो काम करेगा, क्योंकि लक्ष्य ऑब्जेक्ट स्थिर स्टेट के आह्वान में शामिल नहीं है)।

((net.foo.X) null).doSomething();

इसके फायदे हैं

  • साइड-इफ़ेक्ट फ्री (झटपट के साथ एक समस्या net.foo.X),
  • किसी भी चीज़ का नाम बदलने की आवश्यकता नहीं है (इसलिए आप Bजिस नाम से चाहते हैं, उसमें विधि दे सकते हैं; इसीलिए import staticआपके सटीक काम नहीं करेंगे),
  • प्रतिनिधि वर्ग की शुरूआत की आवश्यकता नहीं है (हालांकि यह एक अच्छा विचार हो सकता है ...), और
  • प्रतिबिंब एपीआई के साथ काम करने की आवश्यकता नहीं है।

नकारात्मक पक्ष यह है कि यह कोड वास्तव में भयानक है! मेरे लिए, यह एक चेतावनी उत्पन्न करता है, और यह सामान्य तौर पर एक अच्छी बात है। लेकिन जब से यह एक समस्या के आसपास काम कर रहा है, जो अन्यथा पूरी तरह से अव्यवहारिक है, एक जोड़ना

@SuppressWarnings("static-access")

एक उपयुक्त (न्यूनतम!) एनक्लोजिंग पॉइंट कंपाइलर को बंद कर देगा।


1
क्यों नहीं? आप बस सही काम करेंगे और कोड को रिफलेक्टर करेंगे।
गिम्बी

1
स्टेटिक आयात कि इस समाधान की तुलना में बहुत स्पष्ट नहीं कर रहे हैं और सभी मामलों में नहीं है काम (तुम वहाँ एक है कि अगर खराब कर रहे हैं doSomethingअपने पदानुक्रम में विधि कहीं भी), तो हां सबसे अच्छा समाधान।
वू जूल 4'14

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

1
रचनात्मकता के लिए वोट करें, मैं उत्पादन कोड में ऐसा कभी नहीं करूंगा। मेरा मानना ​​है कि अप्रत्यक्ष इस समस्या का जवाब है (क्या यह सभी समस्याओं के लिए नहीं है?)।
एथनफर

इस समाधान के लिए धन्यवाद डोनल। दरअसल, यह समाधान उन जगहों पर प्रकाश डालता है जहां "टाइप" का उपयोग किया जा सकता है और एक चर का उपयोग नहीं किया जा सकता है। इसलिए, एक "कास्ट" में, कंपाइलर "टाइप" का चयन करेगा, भले ही उसी नाम के साथ कोई चर हो। इस तरह के और दिलचस्प मामलों के लिए, मैं 2 "जावा कमज़ोर" किताबें सुझाएगा : books.google.co.in/books/about/… books.google.co.in/books/about/…
RRM

38

संभवत: इसे प्रबंधित करने का सबसे सरल (जरूरी नहीं सबसे आसान) तरीका एक प्रतिनिधि वर्ग के साथ होगा:

import net.foo.X;
class C {
    static void doSomething() {
         X.doSomething();
    }
}

और फिर ...

class B extends A {
    void doX(){
        C.doSomething();
    }
}

यह कुछ हद तक क्रियात्मक है, लेकिन बहुत लचीला है - आप इसे किसी भी तरह से व्यवहार करने के लिए प्राप्त कर सकते हैं जो आप चाहते हैं; इसके अलावा यह दोनों staticतरीकों और तात्कालिक वस्तुओं के साथ एक ही तरह से काम करता है

यहां प्रतिनिधि वस्तुओं के बारे में अधिक जानकारी: http://en.wikipedia.org/wiki/Delegation_pattern


संभवतः सबसे स्वच्छ समाधान प्रदान किया गया। मैं शायद उस समस्या का उपयोग करूंगा।
लॉर्डऑफइंप्ल्स

37

आप स्थैतिक आयात का उपयोग कर सकते हैं:

import static net.foo.X.doSomething;

class B extends A {
    void doX(){
        doSomething();
    }
}

उस पर ध्यान दें Bऔर Aनाम के तरीके शामिल न करेंdoSomething


क्या net.foo.X.doSomething में केवल पैकेज एक्सेस नहीं है? मतलब आप इसे पैकेज से नहीं पा सकते हैं। कॉम
जेम्स बी बी

2
@JamesB हाँ, लेकिन तब पूर्ण प्रश्न का कोई मतलब नहीं होगा, क्योंकि विधि किसी भी तरह से सुलभ नहीं होगी। मुझे लगता है कि उदाहरण को सरल करते हुए यह एक त्रुटि है
Absurd-Mind

1
लेकिन मूल प्रश्न सक्रिय doSomethingरूप से विधि के नाम के रूप में उपयोग करना चाहता है B...
डोनल फैलो

3
@ डॉनल फेलो: आप सही हैं; यह समाधान काम नहीं करता है यदि मेरे कोड को मेरे पोस्ट के अनुसार बने रहना है। सौभाग्य से, मैं विधि का नाम बदल सकता हूं। हालाँकि, ऐसे मामले के बारे में सोचें जहाँ मेरा तरीका दूसरी विधि से आगे निकल जाता है, तब मैं इसका नाम नहीं बदल सकता। इस मामले में, यह जवाब वास्तव में समस्या को हल नहीं करेगा। लेकिन यह मेरी समस्या से पहले से भी अधिक संयोग होगा, इसलिए शायद कभी भी कोई भी इंसान ऐसा नहीं होगा जो इस समस्या का सामना ट्रिपल नाम के साथ करेगा :)।
gexicide

4
बाकी सभी के लिए इसे स्पष्ट करने के लिए: यह समाधान काम नहीं करता है जैसे ही आपके पास doSomethingवंशानुक्रम पदानुक्रम में कहीं भी है (विधि कॉल लक्ष्यों को कैसे हल किया जाता है)। यदि आप एक toStringविधि को कॉल करना चाहते हैं तो नथ आपकी मदद करें । डोनल द्वारा प्रस्तावित समाधान वही है जो बलोच की जावा पज़लर्स पुस्तक में है (मुझे लगता है, इसे देखा नहीं है), इसलिए हम इसे आधिकारिक उत्तर पर विचार कर सकते हैं :-)
वू

16

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

जावा: वर्ग का newInstance जिसमें कोई डिफ़ॉल्ट कंस्ट्रक्टर नहीं है

और फिर उदाहरण पर विधि लागू करें।

या, केवल प्रतिबिंब के साथ विधि को लागू करें: प्रतिबिंब का उपयोग करके एक स्थिर विधि को लागू करना

Class<?> clazz = Class.forName("net.foo.X");
Method method = clazz.getMethod("doSomething");
Object o = method.invoke(null);

बेशक, ये स्पष्ट रूप से अंतिम रिसॉर्ट हैं।


2
यह उत्तर अब तक का सबसे बड़ा ओवरकिल है - अंगूठे ऊपर :)
gexicide

3
आपको इस उत्तर के लिए एक पूरा करने वाला बिल्ला मिलना चाहिए; आपने उस विलक्षण गरीब व्यक्ति के लिए उत्तर प्रदान किया जिसे जावा के प्राचीन संस्करण का उपयोग करना है और इस सटीक समस्या को हल करना है।
गिम्बी

मैंने वास्तव में इस तथ्य के बारे में नहीं सोचा static importथा कि केवल फीचर को जोड़ा गया था Java 1.5। मैं उन लोगों से ईर्ष्या नहीं करता, जिन्हें 1.4 या उससे नीचे के विकास की आवश्यकता है, मुझे एक बार जाना था और यह बहुत ही भयानक था!
एपिकपांडाफॉर्ल्स

1
@ जिमी: ठीक है, अभी भी ((net.foo.X)null).doSomething()समाधान है जो पुराने जावा में काम करता है। लेकिन अगर टाइप Aमें एक आंतरिक प्रकार भी शामिल है net, तो यह एकमात्र उत्तर है जो मान्य रहता है :)।
15:59 पर gexicide

6

वास्तव में जवाब नहीं है लेकिन आप एक्स का एक उदाहरण बना सकते हैं और उस पर स्थिर विधि को कॉल कर सकते हैं। यह एक तरीका होगा (गंदा मैं मानता हूं) अपनी विधि को कॉल करने के लिए।

(new net.foo.X()).doSomething();

3
nullप्रकार पर कास्टिंग करना और फिर उस पर विधि को कॉल करना बेहतर होगा, क्योंकि ऑब्जेक्ट बनाने से साइड इफेक्ट हो सकते हैं जो मुझे नहीं चाहिए।
gexicide

@gexicide कास्टिंग का विचार nullऐसा करने के लिए क्लीनर तरीकों में से एक प्रतीत होता है। @SuppressWarnings("static-access")चेतावनी-मुक्त होने की जरूरत है ...
डोनाल्ड फेलो

के बारे में सटीक के लिए धन्यवाद null, लेकिन कास्टिंग nullहमेशा मेरे लिए एक दिल तोड़ने वाला रहा है।
माइकल Laffargue

4

किसी भी कास्ट को करने या किसी भी अजीब चेतावनी को दबाने या कोई अनावश्यक उदाहरण बनाने की कोई आवश्यकता नहीं है। बस इस तथ्य का उपयोग करके एक चाल है कि आप उप-वर्ग के माध्यम से माता-पिता के स्थिर तरीकों को कॉल कर सकते हैं। (मेरे हैकिश समाधान के समान है ।)

बस इस तरह एक वर्ग बनाएँ

public final class XX extends X {
    private XX(){
    }
}

(इस अंतिम वर्ग में निजी कंस्ट्रक्टर यह सुनिश्चित करता है कि कोई भी गलती से इस वर्ग का उदाहरण नहीं बना सकता है।)

फिर आप इसके X.doSomething()माध्यम से कॉल करने के लिए स्वतंत्र हैं :

    public class B extends A {

        public void doSomething() {
            XX.doSomething();
        }

दिलचस्प है, फिर भी एक और दृष्टिकोण। हालांकि, स्थिर विधि वाला वर्ग एक अंतिम उपयोगिता वर्ग है।
gexicide

हाँ, इस मामले में इस चाल का उपयोग नहीं कर सकते। फिर भी एक और कारण है कि स्थैतिक विधि एक भाषा विफल है और स्काला के ऑब्जेक्ट दृष्टिकोण को लिया जाना चाहिए।
बिल सीसी.एन.

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

@LordOfThePigs हालांकि यह अतिरिक्त विधि कॉल और भविष्य के रिफैक्टिंग बोझ से बचा जाता है। नया वर्ग मूल रूप से एक उपनाम के रूप में कार्य करता है।
Billc.cn

2

क्या होगा अगर आप gobalnamespace दिए गए सभी फाइलों को एक ही फ़ोल्डर में रखने की कोशिश करते हैं। ( http://www.beanshell.org/javadoc/bsh/class-use/NameSpace.html )

    package com.bar;
      class B extends A {

       public void doSomething(){
         com.bar.getGlobal().net.foo.X.doSomething(); // drill down from the top...

         }
     }

यह कैसे काम करता है? AFAIK getGlobal()पैकेज के लिए एक मानक जावा विधि नहीं है ... (मुझे लगता है कि पैकेजों में जावा में कोई विधि नहीं हो सकती ...)
siegi

1

यह एक कारण है कि रचना विरासत के लिए बेहतर है।

package com.bar;
import java.util.concurrent.Callable;
public class C implements Callable<org.A>
{
    private class B extends org.A{
    public void doSomething(){
        C.this.doSomething();
    }
    }

    private void doSomething(){
    net.foo.X.doSomething();
    }

    public org.A call(){
    return new B();
    }
}

0

मैं रणनीति पैटर्न का उपयोग करूंगा।

public interface SomethingStrategy {

   void doSomething();
}

public class XSomethingStrategy implements SomethingStrategy {

    import net.foo.X;

    @Override
    void doSomething(){
        X.doSomething();
    }
}

class B extends A {

    private final SomethingStrategy strategy;

    public B(final SomethingStrategy strategy){
       this.strategy = strategy;
    }

    public void doSomething(){

        strategy.doSomething();
    }
}

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


आप वर्ग घोषणा implements SomethingStrategyपर जोड़ना भूल गए हैं XSomethingStrategy
रिकार्डो सूजा

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