231 votes

Existe-t-il un moyen de simuler le concept «ami» C ++ en Java?

Je souhaiterais pouvoir écrire une classe Java dans un package capable d'accéder à des méthodes non publiques d'une classe dans un autre package sans avoir à en faire une sous-classe de l'autre classe. Est-ce possible?

573voto

Salomon BRYS Points 676

Voici une petite astuce que j'utilise en JAVA pour répliquer C++ ami mécanisme.

Laisser dit que j'ai une classe Romeo et une autre classe Juliet. Il y a dans les paquets différents (famille), de la haine, des raisons.

Romeo veut cuddle Juliet et Juliet voulez seulement laissez - Romeo cuddle .

En C++, Juliet déclarent Romeo (amant) friend , mais il n'y a pas de telles choses en java.

Voici les classes et le truc :

Ladies first :

package capulet;

import montague.Romeo;

public class Juliet {

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

}

Donc, la méthode de Juliet.cuddle est public , mais vous avez besoin d'un Romeo.Love de l'appeler. Il utilise cette Romeo.Love comme une "signature" de sécurité" pour s'assurer que seuls Romeo peut appeler cette méthode et simplement les appels hashCode sur elle de sorte que le moteur d'exécution jeter un NullPointerException si il est null.

Maintenant les garçons :

package montague;

import capulet.Juliet;

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

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

La classe Romeo.Love est public, mais son constructeur private. Donc tout le monde peut voir, mais seulement en Romeo peut construire. Je utiliser un point de référence fixe donc l' Romeo.Love qui n'est jamais utilisée est construit une fois et n'a pas d'impact de l'optimisation.

Par conséquent, Romeo peut cuddle Juliet et que lui seul peut parce que seul il peut construire et d'accéder à un Romeo.Love de l'instance, qui est requis par Juliet de cuddle (ou sinon elle va te frapper avec un NullPointerException).

59voto

David G Points 3588

Les concepteurs de Java explicitement rejeté l'idée d'un ami car il fonctionne en C++. Vous mettez vos "amis" dans le même package. Privé, protégé et emballé sécurité est appliquée dans le cadre de la conception de langage.

James Gosling voulais Java C++ sans erreurs. Je crois qu'il a senti que l'ami était une erreur parce qu'elle viole les principes de la programmation orientée objet. Paquets fournissent un moyen raisonnable d'organiser les composants sans être trop puriste sur la POO.

NR a fait remarquer que vous pouvez tricher en utilisant la réflexion, mais même cela ne fonctionne que si vous n'utilisez pas le SecurityManager. Si vous activez Java standard de sécurité, vous ne serez pas en mesure de tricher avec la réflexion, à moins que vous écrivez de la politique de sécurité à autoriser expressément.

47voto

Matthew Murdoch Points 11530

La " ami " concept est utile en Java, par exemple, pour séparer une API à partir de sa mise en œuvre. Il est commun pour les classes d'implémentation besoin d'accéder à la classe de l'API internes, mais ils ne devraient pas être exposés à des API clients. Ceci peut être réalisé à l'aide de la "Ami Accesseur' modèle comme détaillé ci-dessous:

La classe exposés par le biais de l'API:

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();
        }
    }
}

La classe 'ami' de la fonctionnalité:

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);
}

Exemple d'accès d'une classe dans le " ami " de la mise en œuvre du paquet:

package impl;

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

11voto

Jeff Axelrod Points 8946

Il y a deux solutions à votre question qui n'impliquent pas de garder toutes les classes du même package.

La première est d'utiliser l'Ami de l'Accesseur/Ami Package de modèle décrit dans (Pratique de Conception d'API, Tulach 2008).

La seconde est d'utiliser OSGi. Il y a un article ici d'expliquer comment OSGi accomplit ce.

Questions connexes: 1, 2, et 3.

8voto

Black Points 3104

Pour autant que je sache, ce n'est pas possible.

Peut-être que vous pourriez nous donner plus de détails sur votre conception. Des questions comme celles-ci sont probablement le résultat de défauts de conception.

Il suffit de considérer

  • Pourquoi ces classes sont-elles dans des packages différents, si elles sont si étroitement liées?
  • A doit-il accéder aux membres privés de B ou l'opération doit-elle être déplacée vers la classe B et déclenchée par A?
  • Est-ce vraiment l'appel ou la gestion des événements est-elle meilleure?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X