क्या जावा में C ++ 'मित्र' अवधारणा का अनुकरण करने का कोई तरीका है?


196

मैं एक पैकेज में एक जावा वर्ग लिखने में सक्षम होना चाहता हूं जो किसी अन्य पैकेज में एक वर्ग के गैर-सार्वजनिक तरीकों को एक्सेस कर सकता है, बिना इसे दूसरे वर्ग का उपवर्ग बनाए बिना। क्या यह संभव है?

जवाबों:


466

यहाँ एक छोटी सी तरकीब है जिसका उपयोग मैं JAVA में C ++ मित्र तंत्र को दोहराने के लिए करता हूँ।

कहते हैं कि मेरे पास एक क्लास Romeoऔर दूसरी क्लास है Juliet। वे नफरत के कारणों के लिए अलग-अलग पैकेज (परिवार) में हैं।

Romeoचाहता है cuddle Julietऔर Julietकेवल Romeo cuddleउसे जाने देना चाहता है ।

C ++ में, एक (प्रेमी) के रूप में Julietघोषित Romeoकिया जाएगा , friendलेकिन जावा में ऐसी चीजें नहीं हैं।

यहाँ कक्षाएं और चाल हैं:

महिलायें पहले :

package capulet;

import montague.Romeo;

public class Juliet {

    public static void cuddle(Romeo.Love love) {
        Objects.requireNonNull(love);
        System.out.println("O Romeo, Romeo, wherefore art thou Romeo?");
    }

}

तो विधि Juliet.cuddleहै, publicलेकिन आपको Romeo.Loveइसे कॉल करने की आवश्यकता है । यह Romeo.Love"हस्ताक्षर सुरक्षा" के रूप में इसका उपयोग यह सुनिश्चित करने के लिए करता है कि केवल Romeoइस पद्धति को कॉल कर सकता है और जांच सकता है कि प्यार वास्तविक है ताकि रनटाइम एक फेंक देगा NullPointerExceptionयदि यह है null

अब लड़के:

package montague;

import capulet.Juliet;

public class Romeo {
    public static final class Love { private Love() {} }
    private static final Love love = new Love();

    public static void cuddleJuliet() {
        Juliet.cuddle(love);
    }
}

वर्ग Romeo.Loveसार्वजनिक है, लेकिन इसका निर्माता है private। इसलिए कोई भी इसे देख सकता है, लेकिन केवल Romeoइसका निर्माण कर सकता है। मैं एक स्थैतिक संदर्भ का उपयोग करता हूं, इसलिए इसका Romeo.Loveउपयोग कभी नहीं किया जाता है केवल एक बार निर्माण किया जाता है और अनुकूलन को प्रभावित नहीं करता है।

इसलिए, Romeoयह कर सकते हैं cuddle Julietऔर केवल वह, क्योंकि केवल वह निर्माण और उपयोग कर सकते हैं कर सकते हैं एक Romeo.Loveउदाहरण है, जो के लिए आवश्यक है Julietकरने के लिए cuddleउसे (या किसी वह आप एक साथ थप्पड़ मारता हूँ NullPointerException)।


107
+1 के लिए "आपको NullPointerException के साथ थप्पड़ मारना"। बहुत प्रभावशाली।
निकोलस

2
@Steazy है: नोटनल, नॉननॉल और चेकफोरनुल एनोटेशन के लिए देखें। उन एनोटेशन का उपयोग करने और उन्हें लागू करने का तरीका जानने के लिए अपने IDE के दस्तावेज़ों की जाँच करें। मुझे पता है कि IntelliJ डिफ़ॉल्ट रूप से इसे एम्बेड करता है और उस ग्रहण को एक प्लगइन की आवश्यकता होती है (जैसे FindBugs)।
सॉलोमन BRYS

27
आप कर सकते हैं Romeos ' Loveके लिए Juliaबदल कर अनन्त loveक्षेत्र होने के लिए final;-)।
मथायस

5
@ माथियास प्रेम क्षेत्र स्थिर है ... मैं इसे अंतिम रूप देने के लिए जवाब संपादित करूंगा;)
सॉलोमन ब्रिक्स

12
हास्य और महान उदाहरण के लिए सभी उत्तर इस तरह (वाई) +1 होना चाहिए।
ज़िया उल रहमान मुग़ल

54

जावा के डिजाइनरों ने मित्र के विचार को स्पष्ट रूप से खारिज कर दिया क्योंकि यह सी ++ में काम करता है। आपने अपने "दोस्तों" को उसी पैकेज में रखा। निजी, संरक्षित और पैकेज्ड सुरक्षा को भाषा डिजाइन के हिस्से के रूप में लागू किया गया है।

जेम्स गोसलिंग चाहते थे कि जावा गलतियों के बिना C ++ हो। मेरा मानना ​​है कि उन्होंने महसूस किया कि दोस्त एक गलती थी क्योंकि यह ओओपी सिद्धांतों का उल्लंघन करता है। पैकेज OOP के बारे में बहुत शुद्ध होने के बिना घटकों को व्यवस्थित करने का एक उचित तरीका प्रदान करते हैं।

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


11
मेरा मतलब एक पेडेंट नहीं है, लेकिन एक्सेस संशोधक एक सुरक्षा तंत्र नहीं हैं।
ग्रेग डी

6
एक्सेस संशोधक जावा सुरक्षा मॉडल का हिस्सा हैं। मैं विशेष रूप से java.lang.RuntimePermission रिफ्लेक्शन के लिए संदर्भित कर रहा था: accessDeclaredMembers और accessClassInPackage।
डेविड जी

54
यदि गोस्लिंग ने वास्तव में सोचा था कि friendओओपी का उल्लंघन किया था (विशेष रूप से, पैकेज एक्सेस से अधिक) तो वह वास्तव में इसे समझ नहीं पाया (पूरी तरह से संभव है, बहुत से लोग इसे गलत समझते हैं)।
कोनराड रुडोल्फ

8
वर्ग घटकों को कभी-कभी अलग करने की आवश्यकता होती है (जैसे कार्यान्वयन और एपीआई, कोर ऑब्जेक्ट और एडेप्टर)। पैकेज-स्तर की सुरक्षा एक ही समय में है और इसे ठीक से करने के लिए भी अनुमति है।
दिनांक १dy

2
@GregD उन्हें इस अर्थ में एक सुरक्षा तंत्र माना जा सकता है कि वे डेवलपर्स को गलत तरीके से एक वर्ग के सदस्य का उपयोग करने से रोकने में मदद करते हैं। मुझे लगता है कि वे शायद एक सुरक्षा तंत्र के रूप में संदर्भित होंगे ।
क्रश करें

45

जावा में 'दोस्त' की अवधारणा उपयोगी है, उदाहरण के लिए, इसके कार्यान्वयन से एपीआई को अलग करने के लिए। कार्यान्वयन कक्षाओं के लिए एपीआई क्लास इंटर्नल्स तक पहुंच की आवश्यकता होती है, लेकिन इन्हें एपीआई ग्राहकों के संपर्क में नहीं आना चाहिए। यह नीचे दिए गए विवरण के अनुसार 'फ्रेंड एक्सेसर' पैटर्न का उपयोग करके प्राप्त किया जा सकता है:

एपीआई के माध्यम से उजागर किया गया वर्ग:

package api;

public final class Exposed {
    static {
        // Declare classes in the implementation package as 'friends'
        Accessor.setInstance(new AccessorImpl());
    }

    // Only accessible by 'friend' classes.
    Exposed() {

    }

    // Only accessible by 'friend' classes.
    void sayHello() {
        System.out.println("Hello");
    }

    static final class AccessorImpl extends Accessor {
        protected Exposed createExposed() {
            return new Exposed();
        }

        protected void sayHello(Exposed exposed) {
            exposed.sayHello();
        }
    }
}

'मित्र' कार्यक्षमता प्रदान करने वाला वर्ग:

package impl;

public abstract class Accessor {

    private static Accessor instance;

    static Accessor getInstance() {
        Accessor a = instance;
        if (a != null) {
            return a;
        }

        return createInstance();
    }

    private static Accessor createInstance() {
        try {
            Class.forName(Exposed.class.getName(), true, 
                Exposed.class.getClassLoader());
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e);
        }

        return instance;
    }

    public static void setInstance(Accessor accessor) {
        if (instance != null) {
            throw new IllegalStateException(
                "Accessor instance already set");
        }

        instance = accessor;
    }

    protected abstract Exposed createExposed();

    protected abstract void sayHello(Exposed exposed);
}

'मित्र' कार्यान्वयन पैकेज में एक वर्ग से उदाहरण का उपयोग:

package impl;

public final class FriendlyAccessExample {
    public static void main(String[] args) {
        Accessor accessor = Accessor.getInstance();
        Exposed exposed = accessor.createExposed();
        accessor.sayHello(exposed);
    }
}

1
क्योंकि मुझे नहीं पता था कि "एक्सपोज़्ड" क्लास में "स्टैटिक" का क्या मतलब है: स्टैटिक ब्लॉक, एक जावा क्लास के अंदर स्टेटमेंट का एक ब्लॉक है जिसे तब निष्पादित किया जाएगा जब एक क्लास को पहली बार JVM में लोड किया जाता है । javatutorialhub
गाय एल

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

8
मैं अपने जावा पर काफी कठोर हूँ, इसलिए अपने अज्ञान को क्षमा करें। "रोमियो एंड जूलियट" समाधान सॉलोमन BRYS पोस्ट पर इसका क्या फायदा है? अगर मैं इस पर एक कोड बेस में अटका रहा (बिना आपकी व्याख्या के, यानी भारी-भरकम टिप्पणी), तो यह क्रियान्वयन मुझे डरा देगा। रोमियो और जूलियट का दृष्टिकोण समझने में बहुत सरल है।
20

1
यह दृष्टिकोण केवल रनटाइम के दौरान समस्याओं को दिखाई देगा, जबकि रोमियो और जूलियट के दुरुपयोग ने उन्हें विकसित होते समय संकलन समय पर दिखाई देगा।
यमजोरोस

1
@ymajoros रोमियो और जूलियट का उदाहरण संकलन-समय पर दुरुपयोग को दिखाई नहीं देता है। यह एक तर्क को सही ढंग से पारित किया जा रहा है, और एक अपवाद को फेंक दिया जा रहा है। वे दोनों रन-टाइम क्रियाएं हैं।
रेडियोडेफ

10

आपके प्रश्न के दो समाधान हैं जो सभी वर्गों को एक ही पैकेज में रखने में शामिल नहीं हैं।

सबसे पहले फ्रेंड एक्सेसर / फ्रेंड पैकेज का उपयोग करना है पैटर्न (प्रेक्टिकल एपीआई डिज़ाइन, टुलक 2008) में वर्णित है।

दूसरा है OSGi का उपयोग करना। यहाँ एक लेख है जिसमें बताया गया है कि OSGi इसे कैसे पूरा करता है।

संबंधित प्रश्न: 1 , 2 , और 3


7

जहां तक ​​मुझे पता है, यह संभव नहीं है।

हो सकता है, आप हमें अपने डिजाइन के बारे में कुछ और जानकारी दे सकें। इस तरह के प्रश्न संभवतया डिज़ाइन दोषों के परिणाम हैं।

जरा विचार करें

  • अलग-अलग पैकेजों में वे कक्षाएं क्यों हैं, अगर वे इतनी बारीकी से संबंधित हैं?
  • क्या A को B के निजी सदस्यों तक पहुंच प्राप्त है या ऑपरेशन को कक्षा B में ले जाना चाहिए और A द्वारा ट्रिगर किया जाना चाहिए?
  • क्या यह वास्तव में कॉलिंग है या इवेंट-हैंडलिंग बेहतर है?

3

इरिक्मा का जवाब आसान और उत्कृष्ट है। मैं एक और बात जोड़ सकता हूं: एक सार्वजनिक रूप से सुलभ विधि होने के बजाय, एक दोस्त पाने के लिए getFriend () का उपयोग करें जिसे आप उपयोग नहीं कर सकते हैं, आप एक कदम आगे बढ़ सकते हैं और एक टोकन के बिना दोस्त को प्राप्त करने से मना कर सकते हैं: getFriend (Service.FriadToken)। यह फ्रेंडटोकेन एक निजी कंस्ट्रक्टर के साथ एक आंतरिक सार्वजनिक वर्ग होगा, ताकि केवल सेवा ही किसी को तुरंत दे सके।


3

यहां एक पुन: प्रयोज्य के साथ एक स्पष्ट उपयोग-मामला उदाहरण है Friend वर्ग के । इस तंत्र का लाभ उपयोग की सादगी है। हो सकता है कि यूनिट टेस्ट कक्षाओं को बाकी एप्लिकेशन की तुलना में अधिक एक्सेस देने के लिए अच्छा हो।

शुरू करने के लिए, यहां एक उदाहरण है कि Friendकक्षा का उपयोग कैसे किया जाए ।

public class Owner {
    private final String member = "value";

    public String getMember(final Friend friend) {
        // Make sure only a friend is accepted.
        friend.is(Other.class);
        return member;
    }
}

फिर दूसरे पैकेज में आप ऐसा कर सकते हैं:

public class Other {
    private final Friend friend = new Friend(this);

    public void test() {
        String s = new Owner().getMember(friend);
        System.out.println(s);
    }
}

Friendवर्ग इस प्रकार है।

public final class Friend {
    private final Class as;

    public Friend(final Object is) {
        as = is.getClass();
    }

    public void is(final Class c) {
        if (c == as)
            return;
        throw new ClassCastException(String.format("%s is not an expected friend.", as.getName()));
    }

    public void is(final Class... classes) {
        for (final Class c : classes)
            if (c == as)
                return;
        is((Class)null);
    }
}

हालाँकि, समस्या यह है कि इसका दुरुपयोग किया जा सकता है:

public class Abuser {
    public void doBadThings() {
        Friend badFriend = new Friend(new Other());
        String s = new Owner().getMember(badFriend);
        System.out.println(s);
    }
}

अब, यह सच हो सकता है कि Otherवर्ग के पास कोई सार्वजनिक निर्माता नहीं है, इसलिए उपरोक्त Abuserकोड को असंभव बना देता है । हालांकि, अपने वर्ग करता है, तो करता है एक सार्वजनिक निर्माता है तो यह शायद उचित एक आंतरिक वर्ग के रूप में मित्र वर्ग नकल करने के लिए है। इस Other2वर्ग को एक उदाहरण के रूप में लें :

public class Other2 {
    private final Friend friend = new Friend();

    public final class Friend {
        private Friend() {}
        public void check() {}
    }

    public void test() {
        String s = new Owner2().getMember(friend);
        System.out.println(s);
    }
}

और तब Owner2वर्ग इस तरह होगा:

public class Owner2 {
    private final String member = "value";

    public String getMember(final Other2.Friend friend) {
        friend.check();
        return member;
    }
}

ध्यान दें कि Other2.Friendकक्षा में एक निजी कंस्ट्रक्टर है, इस प्रकार यह इसे करने का एक अधिक सुरक्षित तरीका है।


2

प्रदान किया गया समाधान शायद सबसे सरल नहीं था। एक अन्य दृष्टिकोण C ++ के समान विचार पर आधारित है: निजी सदस्य पैकेज / निजी दायरे के बाहर सुलभ नहीं होते हैं, केवल एक विशिष्ट वर्ग को छोड़कर जो मालिक खुद का मित्र बनाता है।

जिस वर्ग को किसी सदस्य के लिए मित्र पहुंच की आवश्यकता होती है, उसे एक आंतरिक सार्वजनिक सार "मित्र वर्ग" बनाना चाहिए, जो कि छिपी हुई संपत्तियों के मालिक वर्ग पहुंच-निर्यात के तरीकों को लागू करने वाले उपवर्ग को लौटाकर, निर्यात का उपयोग कर सकता है। मित्र वर्ग की "एपीआई" विधि निजी हो सकती है, इसलिए यह उस कक्षा के बाहर सुलभ नहीं है जिसे मित्र की आवश्यकता है। इसका एकमात्र कथन एक अमूर्त संरक्षित सदस्य के लिए एक कॉल है जो निर्यात वर्ग को लागू करता है।

यहाँ कोड है:

पहला परीक्षण जो पुष्टि करता है कि यह वास्तव में काम करता है:

package application;

import application.entity.Entity;
import application.service.Service;
import junit.framework.TestCase;

public class EntityFriendTest extends TestCase {
    public void testFriendsAreOkay() {
        Entity entity = new Entity();
        Service service = new Service();
        assertNull("entity should not be processed yet", entity.getPublicData());
        service.processEntity(entity);
        assertNotNull("entity should be processed now", entity.getPublicData());
    }
}

तब वह सेवा जिसके लिए इकाई के एक निजी सदस्य को मित्र की आवश्यकता है:

package application.service;

import application.entity.Entity;

public class Service {

    public void processEntity(Entity entity) {
        String value = entity.getFriend().getEntityPackagePrivateData();
        entity.setPublicData(value);
    }

    /**
     * Class that Entity explicitly can expose private aspects to subclasses of.
     * Public, so the class itself is visible in Entity's package.
     */
    public static abstract class EntityFriend {
        /**
         * Access method: private not visible (a.k.a 'friendly') outside enclosing class.
         */
        private String getEntityPackagePrivateData() {
            return getEntityPackagePrivateDataImpl();
        }

        /** contribute access to private member by implementing this */
        protected abstract String getEntityPackagePrivateDataImpl();
    }
}

अंत में: इकाई वर्ग जो केवल पैकेज Application.service.Service के लिए एक पैकेज निजी सदस्य के लिए अनुकूल पहुँच प्रदान करता है।

package application.entity;

import application.service.Service;

public class Entity {

    private String publicData;
    private String packagePrivateData = "secret";   

    public String getPublicData() {
        return publicData;
    }

    public void setPublicData(String publicData) {
        this.publicData = publicData;
    }

    String getPackagePrivateData() {
        return packagePrivateData;
    }

    /** provide access to proteced method for Service'e helper class */
    public Service.EntityFriend getFriend() {
        return new Service.EntityFriend() {
            protected String getEntityPackagePrivateDataImpl() {
                return getPackagePrivateData();
            }
        };
    }
}

ठीक है, मुझे मानना ​​होगा कि यह "मित्र सेवा :: सेवा;" की तुलना में थोड़ा लंबा है लेकिन एनोटेशन का उपयोग करके संकलन-समय की जाँच को बनाए रखते हुए इसे छोटा करना संभव हो सकता है।


यह एक ही पैकेज में एक सामान्य वर्ग के रूप में काफी काम नहीं करता है बस getFriend () कर सकता है और फिर निजी को दरकिनार करके संरक्षित पद्धति को कॉल कर सकता है।
2022 बजे user2219808

1

जावा में "पैकेज-संबंधित मित्रता" होना संभव है। यह यूनिट टेस्टिंग के लिए यूजरफुल हो सकता है। यदि आप एक विधि के सामने निजी / सार्वजनिक / संरक्षित निर्दिष्ट नहीं करते हैं, तो यह "पैकेज में मित्र" होगा। उसी पैकेज में एक क्लास इसे एक्सेस करने में सक्षम होगी, लेकिन यह क्लास के बाहर निजी होगी।

यह नियम हमेशा ज्ञात नहीं है, और यह C ++ "मित्र" कीवर्ड का एक अच्छा अनुमान है। मुझे यह एक अच्छा प्रतिस्थापन लगता है।


1
यह सच है, लेकिन मैं वास्तव में अलग-अलग पैकेजों में रहने वाले कोड के बारे में पूछ रहा था ...
मैथ्यू मर्डोक

1

मुझे लगता है कि C ++ में मित्र कक्षाएं जावा में इनर-क्लास अवधारणा की तरह हैं। आंतरिक कक्षाओं का उपयोग करके आप वास्तव में एक संलग्न वर्ग और एक संलग्न को परिभाषित कर सकते हैं। संलग्न वर्ग की सार्वजनिक और निजी सदस्यों तक पूरी पहुंच है, जो इसे वर्ग को घेर रहा है। निम्नलिखित लिंक देखें: http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html


एर, नहीं, वे नहीं हैं। यह वास्तविक जीवन में दोस्ती की तरह है: यह हो सकता है, लेकिन पारस्परिक होने की आवश्यकता नहीं है (A, B का मित्र होने का अर्थ B का A का मित्र नहीं माना जाता है), और आप और आपके मित्र बिलकुल अलग हो सकते हैं परिवारों और अपने खुद के, संभवतः (लेकिन जरूरी नहीं) दोस्तों के मंडलियों को ओवरलैप कर रहे हैं। (ऐसा नहीं है कि मैं बहुत सारे दोस्तों के साथ कक्षाएं देखना चाहता हूं। यह एक उपयोगी विशेषता हो सकती है, लेकिन सावधानी के साथ इस्तेमाल किया जाना चाहिए।)
क्रिस्टोफर क्रुट्ज़िग

1

मुझे लगता है, मित्र एक्सेसर पैटर्न का उपयोग करने का तरीका बहुत जटिल है। मुझे उसी समस्या का सामना करना पड़ा और मैंने जावा में C ++ से ज्ञात अच्छे, पुराने कॉपी कंस्ट्रक्टर का उपयोग करके हल किया:

public class ProtectedContainer {
    protected String iwantAccess;

    protected ProtectedContainer() {
        super();
        iwantAccess = "Default string";
    }

    protected ProtectedContainer(ProtectedContainer other) {
        super();
        this.iwantAccess = other.iwantAccess;
    }

    public int calcSquare(int x) {
        iwantAccess = "calculated square";
        return x * x;
    }
}

अपने आवेदन में आप निम्नलिखित कोड लिख सकते हैं:

public class MyApp {

    private static class ProtectedAccessor extends ProtectedContainer {

        protected ProtectedAccessor() {
            super();
        }

        protected PrivateAccessor(ProtectedContainer prot) {
            super(prot);
        }

        public String exposeProtected() {
            return iwantAccess;
        }
    }
}

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

जब भी आपको ProtectedContainer के उदाहरणों से निपटना होता है तो आप अपने ProtectedAccessor को इसके चारों ओर लपेट सकते हैं और आप पहुंच प्राप्त कर सकते हैं।

यह संरक्षित विधियों के साथ भी काम करता है। आप उन्हें अपने एपीआई में संरक्षित परिभाषित करते हैं। बाद में आपके आवेदन में आप एक निजी रैपर क्लास लिखते हैं और संरक्षित पद्धति को सार्वजनिक करते हैं। बस।


1
लेकिन ProtectedContainerपैकेज के बाहर उपवर्ग किया जा सकता है!
राफेल

0

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

जहां तक ​​निजी तरीकों का सवाल है (मुझे लगता है) आप भाग्य से बाहर हैं।


0

मैं मानता हूं कि ज्यादातर मामलों में मित्र कीवर्ड अनावश्यक है।

  • पैकेज-प्राइवेट (उर्फ डिफॉल्ट) ज्यादातर मामलों में पर्याप्त होता है, जहां आपके पास भारी इंटरवेटिड क्लासेस का समूह होता है
  • डीबग कक्षाओं के लिए जो इंटर्नल्स तक पहुंच चाहते हैं, मैं आमतौर पर विधि को निजी बनाता हूं और इसे प्रतिबिंब के माध्यम से एक्सेस करता हूं। गति आमतौर पर यहाँ महत्वपूर्ण नहीं है
  • कभी-कभी, आप एक ऐसी विधि लागू करते हैं जो "हैक" है या अन्यथा जो परिवर्तन के अधीन है। मैं इसे सार्वजनिक करता हूं, लेकिन यह इंगित करने के लिए @Deprecated का उपयोग करता हूं कि आपको इस पद्धति पर भरोसा नहीं करना चाहिए।

और अंत में, यदि यह वास्तव में आवश्यक है, तो अन्य उत्तरों में उल्लिखित मित्र एक्सेसर पैटर्न है।


0

एक खोजशब्द या तो का उपयोग नहीं।

आप प्रतिबिंब आदि का उपयोग करके "धोखा" दे सकते हैं, लेकिन मैं "धोखा" देने की सलाह नहीं दूंगा।


3
मैं इसे इतना बुरा विचार मानूंगा कि यह सुझाव देना भी मेरे लिए घृणित है। जाहिर है कि यह सबसे अच्छा है और किसी भी डिजाइन का हिस्सा नहीं होना चाहिए।
शीस्टमीयर

0

इस समस्या को हल करने के लिए मैंने एक तरीका खोजा है, एक एक्सेसर ऑब्जेक्ट बनाने के लिए, जैसे:

class Foo {
    private String locked;

    /* Anyone can get locked. */
    public String getLocked() { return locked; }

    /* This is the accessor. Anyone with a reference to this has special access. */
    public class FooAccessor {
        private FooAccessor (){};
        public void setLocked(String locked) { Foo.this.locked = locked; }
    }
    private FooAccessor accessor;

    /** You get an accessor by calling this method. This method can only
     * be called once, so calling is like claiming ownership of the accessor. */
    public FooAccessor getAccessor() {
        if (accessor != null)
            throw new IllegalStateException("Cannot return accessor more than once!");
        return accessor = new FooAccessor();
    }
}

कॉल करने वाला पहला कोड getAccessor()एक्सेसर के "स्वामित्व का दावा करता है"। आमतौर पर, यह कोड है जो ऑब्जेक्ट बनाता है।

Foo bar = new Foo(); //This object is safe to share.
FooAccessor barAccessor = bar.getAccessor(); //This one is not.

यह भी, क्योंकि यह एक पर पहुंच को सीमित करते करने की अनुमति देता से अधिक सी ++ के दोस्त तंत्र एक फायदा है प्रति-उदाहरण के रूप में एक करने का विरोध किया, स्तर प्रति वर्ग स्तर। एक्सेसर संदर्भ को नियंत्रित करके, आप ऑब्जेक्ट तक पहुंच को नियंत्रित करते हैं। आप कई एक्सेसर्स भी बना सकते हैं, और प्रत्येक को अलग-अलग एक्सेस दे सकते हैं, जो इस बात पर ठीक-ठीक नियंत्रण देता है कि कौन सा कोड क्या एक्सेस कर सकता है:

class Foo {
    private String secret;
    private String locked;

    /* Anyone can get locked. */
    public String getLocked() { return locked; }

    /* Normal accessor. Can write to locked, but not read secret. */
    public class FooAccessor {
        private FooAccessor (){};
        public void setLocked(String locked) { Foo.this.locked = locked; }
    }
    private FooAccessor accessor;

    public FooAccessor getAccessor() {
        if (accessor != null)
            throw new IllegalStateException("Cannot return accessor more than once!");
        return accessor = new FooAccessor();
    }

    /* Super accessor. Allows access to secret. */
    public class FooSuperAccessor {
        private FooSuperAccessor (){};
        public String getSecret() { return Foo.this.secret; }
    }
    private FooSuperAccessor superAccessor;

    public FooSuperAccessor getAccessor() {
        if (superAccessor != null)
            throw new IllegalStateException("Cannot return accessor more than once!");
        return superAccessor = new FooSuperAccessor();
    }
}

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

class Foo {
    private String secret;
    private String locked;

    public String getLocked() { return locked; }

    public class FooAccessor {
        private FooAccessor (){};
        public void setLocked(String locked) { Foo.this.locked = locked; }
    }
    public class FooSuperAccessor {
        private FooSuperAccessor (){};
        public String getSecret() { return Foo.this.secret; }
    }
    public class FooReference {
        public final Foo foo;
        public final FooAccessor accessor;
        public final FooSuperAccessor superAccessor;

        private FooReference() {
            this.foo = Foo.this;
            this.accessor = new FooAccessor();
            this.superAccessor = new FooSuperAccessor();
        }
    }

    private FooReference reference;

    /* Beware, anyone with this object has *all* the accessors! */
    public FooReference getReference() {
        if (reference != null)
            throw new IllegalStateException("Cannot return reference more than once!");
        return reference = new FooReference();
    }
}

बहुत सिर-पीटने के बाद (अच्छी तरह से नहीं), यह मेरा अंतिम समाधान था, और मुझे यह बहुत पसंद है। यह लचीला है, उपयोग करने में सरल है, और कक्षा की पहुंच पर बहुत अच्छे नियंत्रण की अनुमति देता है। ( संदर्भ के साथ केवल पहुंच बहुत उपयोगी है।) यदि आप एक्सेसर्स / संदर्भों के लिए निजी के बजाय संरक्षित का उपयोग करते हैं, तो फू के उप-वर्ग भी विस्तारित संदर्भ वापस कर सकते हैं getReference। इसके लिए किसी भी प्रतिबिंब की आवश्यकता नहीं होती है, इसलिए इसका उपयोग किसी भी वातावरण में किया जा सकता है।


0

जावा 9 के रूप में, कई मामलों में इसे गैर-मुद्दा बनाने के लिए मॉड्यूल का उपयोग किया जा सकता है।


0

मैं इसे सार्वजनिक वर्ग बनाने से बचने के लिए प्रतिनिधिमंडल या रचना या फैक्ट्री क्लास (इस समस्या के परिणामस्वरूप होने वाली समस्या के आधार पर) को प्राथमिकता देता हूं।

यदि यह "इंटरफ़ेस / कार्यान्वयन कक्षाएं विभिन्न पैकेजों में" समस्या है, तो मैं एक सार्वजनिक कारखाना वर्ग का उपयोग करूंगा जो कि निहित पैकेज के समान पैकेज में होगा और अंतर्निहित वर्ग के जोखिम को रोक देगा।

यदि यह "मुझे इस वर्ग / पद्धति को सार्वजनिक करने से नफरत है, तो यह एक अलग पैकेज में किसी अन्य वर्ग के लिए यह कार्यक्षमता प्रदान करने के लिए" समस्या है, तो मैं एक ही पैकेज में एक सार्वजनिक प्रतिनिधि वर्ग का उपयोग करूंगा और कार्यक्षमता के केवल उस हिस्से को उजागर करूंगा "बाहरी व्यक्ति" वर्ग द्वारा आवश्यक।

इनमें से कुछ निर्णय लक्ष्य सर्वर क्लास लोडिंग आर्किटेक्चर (ओएसजीआई बंडल, डब्ल्यूएआर / ईएआर, आदि), तैनाती और पैकेज नामकरण सम्मेलनों द्वारा संचालित होते हैं। उदाहरण के लिए, उपरोक्त प्रस्तावित समाधान, सामान्य जावा अनुप्रयोगों के लिए 'फ्रेंड एक्सेसर' पैटर्न चतुर है। मुझे आश्चर्य है कि अगर क्लासॉस्टिंग स्टाइल में अंतर के कारण इसे ओएसजीआई में लागू करने के लिए मुश्किल हो जाता है।


0

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

मैंने एक इंटरफ़ेस (व्यवस्थापन) बनाया।

हर वर्ग जो कॉल करने में सक्षम होना चाहिए कहा गया है कि कार्यों को लागू करना चाहिए।

फिर मैंने एक फ़ंक्शन HasAdminRights को इस प्रकार बनाया:

private static final boolean HasAdminRights()
{
    // Gets the current hierarchy of callers
    StackTraceElement[] Callers = new Throwable().getStackTrace();

    // Should never occur with me but if there are less then three StackTraceElements we can't check
    if (Callers.length < 3)
    {
        EE.InvalidCode("Couldn't check for administrator rights");
        return false;

    } else try
    {

        // Now we check the third element as this function is the first and the function wanting to check for the rights the second. We try to use it as a subclass of AdminRights.
        Class.forName(Callers[2].getClassName()).asSubclass(AdminRights.class);

        // If everything worked up to now, it has admin rights!
        return true;

    } catch (java.lang.ClassCastException | ClassNotFoundException e)
    {
        // In the catch, something went wrong and we can deduce that the caller has no admin rights

        EE.InvalidCode(Callers[1].getClassName() + " doesn't have administrator rights");
        return false;
    }
}

-1

मैंने एक बार एक प्रतिबिंब आधारित समाधान देखा, जो प्रतिबिंब का उपयोग करके रनटाइम पर "मित्र जाँच" किया और कॉल स्टैक की जाँच करके यह देखने के लिए कि क्या विधि को कॉल करने वाली कक्षा को ऐसा करने की अनुमति दी गई थी। रनटाइम चेक होने के नाते, इसमें स्पष्ट खामी है।

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