Je voudrais pouvoir écrire une classe Java dans un package qui peut accéder aux 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?
Voici une petite astuce que j'utilise dans JAVA pour répliquer le mécanisme ami C ++.
Disons que j'ai une classe Romeo
et une autre classe Juliet
. Ils sont dans des packages différents (famille) pour des raisons de haine.
Romeo
veut cuddle
Juliet
et Juliet
veut seulement la laisser Romeo
cuddle
.
En C ++, Juliet
se déclarer Romeo
comme (amoureux) friend
mais il n'y a rien de tel en java.
Voici les cours et l'astuce:
Les dames d'abord :
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?");
}
}
Donc, la méthode Juliet.cuddle
est public
mais vous avez besoin d'un Romeo.Love
pour l'appeler. Il l'utilise Romeo.Love
comme une "sécurité de signature" pour s'assurer que seul Romeo
peut appeler cette méthode et vérifie que l'amour est réel afin que le runtime lance un NullPointerException
si c'est le cas null
.
Maintenant les garçons:
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);
}
}
La classe Romeo.Love
est publique, mais son constructeur l'est private
. Par conséquent, n'importe qui peut le voir, mais seul Romeo
peut le construire. J'utilise une référence statique afin Romeo.Love
que celle qui n'est jamais utilisée ne soit construite qu'une seule fois et n'impacte pas l'optimisation.
Par conséquent, ne Romeo
peut cuddle
Juliet
et ne peut - il parce que lui seul peut construire et accéder à une Romeo.Love
instance, qui est requis par Juliet
la cuddle
lui (ou bien elle va te frapper avec NullPointerException
).
Romeo
« sLove
pourJulia
éternelle en changeant lelove
champ pour êtrefinal
;-).Les concepteurs de Java ont explicitement rejeté l'idée de l'ami car elle fonctionne en C ++. Vous mettez vos "amis" dans le même paquet. La sécurité privée, protégée et intégrée est appliquée dans le cadre de la conception du langage.
James Gosling voulait que Java soit C ++ sans les erreurs. Je pense qu'il pensait que cet ami était une erreur parce qu'elle violait les principes de la POO. Les packages fournissent un moyen raisonnable d'organiser les composants sans être trop puristes sur la POO.
NR a souligné que vous pouviez tricher en utilisant la réflexion, mais même cela ne fonctionne que si vous n'utilisez pas SecurityManager. Si vous activez la sécurité standard Java, vous ne pourrez pas tricher avec réflexion à moins que vous n'écriviez une stratégie de sécurité pour l'autoriser spécifiquement.
la source
friend
violait la POO (en particulier, plus que l'accès aux packages), il ne le comprenait vraiment pas (tout à fait possible, beaucoup de gens le comprennent mal).Le concept «ami» est utile en Java, par exemple, pour séparer une API de son implémentation. Il est courant que les classes d'implémentation aient besoin d'accéder aux composants internes de classe API, mais ceux-ci ne doivent pas être exposés aux clients API. Ceci peut être réalisé en utilisant le modèle 'Friend Accessor' comme détaillé ci-dessous:
La classe exposée via l'API:
La classe offrant la fonctionnalité «ami»:
Exemple d'accès à partir d'une classe dans le package d'implémentation «ami»:
la source
Il existe deux solutions à votre question qui n'impliquent pas de conserver toutes les classes dans le même package.
La première consiste à utiliser le modèle Friend Accessor / Friend Package décrit dans (Practical API Design, Tulach 2008).
La seconde consiste à utiliser OSGi. Il y a un article ici expliquant comment OSGi y parvient.
Questions connexes: 1 , 2 et 3 .
la source
Pour autant que je sache, ce n'est pas possible.
Peut-être pourriez-vous nous donner plus de détails sur votre conception. De telles questions sont probablement le résultat de défauts de conception.
Considérez juste
la source
La réponse d'eirikma est simple et excellente. Je pourrais ajouter encore une chose: au lieu d'avoir une méthode accessible au public, getFriend () pour obtenir un ami qui ne peut pas être utilisé, vous pouvez aller plus loin et interdire d'obtenir l'ami sans jeton: getFriend (Service.FriendToken). Ce FriendToken serait une classe publique interne avec un constructeur privé, de sorte que seul le service pourrait en instancier une.
la source
Voici un exemple de cas d'utilisation clair avec une
Friend
classe réutilisable . L'avantage de ce mécanisme est la simplicité d'utilisation. Peut-être bon pour donner aux classes de tests unitaires plus d'accès que le reste de l'application.Pour commencer, voici un exemple d'utilisation de la
Friend
classe.Ensuite, dans un autre package, vous pouvez le faire:
La
Friend
classe est la suivante.Cependant, le problème est qu'il peut être abusé comme suit:
Maintenant, il peut être vrai que la
Other
classe n'a pas de constructeurs publics, ce qui rend leAbuser
code ci-dessus impossible. Toutefois, si votre classe ne un constructeur public alors il est probablement préférable de dupliquer la classe ami comme une classe interne. Prenez ceOther2
cours comme exemple:Et puis la
Owner2
classe serait comme ça:Notez que la
Other2.Friend
classe a un constructeur privé, ce qui en fait un moyen beaucoup plus sûr de le faire.la source
La solution proposée n'était peut-être pas la plus simple. Une autre approche est basée sur la même idée qu'en C ++: les membres privés ne sont pas accessibles en dehors de la portée package / private, à l'exception d'une classe spécifique que le propriétaire se fait un ami.
La classe qui a besoin d'un accès ami à un membre doit créer une "classe ami" abstraite publique intérieure à laquelle la classe possédant les propriétés masquées peut exporter l'accès, en renvoyant une sous-classe qui implémente les méthodes d'implémentation d'accès. La méthode "API" de la classe d'amis peut être privée, elle n'est donc pas accessible en dehors de la classe qui a besoin d'un accès ami. Sa seule instruction est un appel à un membre protégé abstrait que la classe exportatrice implémente.
Voici le code:
D'abord le test qui vérifie que cela fonctionne réellement:
Ensuite, le service qui a besoin d'un accès ami à un package membre privé de l'entité:
Enfin: la classe Entity qui fournit un accès convivial à un membre privé du package uniquement à la classe application.service.Service.
D'accord, je dois admettre que c'est un peu plus long que "friend service :: Service;" mais il pourrait être possible de le raccourcir tout en conservant la vérification à la compilation en utilisant des annotations.
la source
En Java, il est possible d'avoir une "convivialité liée au package". Cela peut être utile pour les tests unitaires. Si vous ne spécifiez pas privé / public / protégé devant une méthode, ce sera "ami dans le package". Une classe dans le même package pourra y accéder, mais elle sera privée en dehors de la classe.
Cette règle n'est pas toujours connue, et c'est une bonne approximation d'un mot-clé "ami" C ++. Je le trouve un bon remplacement.
la source
Je pense que les classes d'amis en C ++ sont comme le concept de classe interne en Java. En utilisant des classes internes, vous pouvez réellement définir une classe englobante et une classe fermée. La classe fermée a un accès complet aux membres publics et privés de sa classe enfermante. voir le lien suivant: http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
la source
Je pense que l'approche de l'utilisation du modèle d'accesseur ami est beaucoup trop compliquée. J'ai dû faire face au même problème et j'ai résolu en utilisant le bon vieux constructeur de copie, connu de C ++, en Java:
Dans votre application, vous pouvez écrire le code suivant:
L'avantage de cette méthode est que seule votre application a accès aux données protégées. Ce n'est pas exactement une substitution du mot-clé ami. Mais je pense que cela convient parfaitement lorsque vous écrivez des bibliothèques personnalisées et que vous devez accéder à des données protégées.
Chaque fois que vous devez gérer des instances de ProtectedContainer, vous pouvez envelopper votre ProtectedAccessor et vous y avez accès.
Il fonctionne également avec des méthodes protégées. Vous les définissez protégés dans votre API. Plus loin dans votre application, vous écrivez une classe wrapper privée et exposez la méthode protégée comme publique. C'est tout.
la source
ProtectedContainer
peut être sous-classé en dehors du package!Si vous souhaitez accéder aux méthodes protégées, vous pouvez créer une sous-classe de la classe que vous souhaitez utiliser qui expose les méthodes que vous souhaitez utiliser en tant que public (ou interne à l'espace de noms pour être plus sûr), et avoir une instance de cette classe dans votre classe (utilisez-le comme proxy).
En ce qui concerne les méthodes privées (je pense), vous n'avez pas de chance.
la source
Je suis d'accord que dans la plupart des cas, le mot-clé ami n'est pas nécessaire.
Et enfin, si c'est vraiment nécessaire, il y a le modèle d'accesseur ami mentionné dans les autres réponses.
la source
Ne pas utiliser de mot-clé.
Vous pouvez "tricher" en utilisant la réflexion, etc., mais je ne recommanderais pas de "tricher".
la source
Une méthode que j'ai trouvée pour résoudre ce problème est de créer un objet accesseur, comme ceci:
Le premier code à appeler
getAccessor()
"revendique la propriété" de l'accesseur. Habituellement, c'est du code qui crée l'objet.Cela a également un avantage sur le mécanisme ami de C ++, car il vous permet de limiter l'accès à un niveau par instance , par opposition à un niveau par classe . En contrôlant la référence de l'accesseur, vous contrôlez l'accès à l'objet. Vous pouvez également créer plusieurs accesseurs et donner un accès différent à chacun, ce qui permet un contrôle précis sur quel code peut accéder à quoi:
Enfin, si vous souhaitez que les choses soient un peu plus organisées, vous pouvez créer un objet de référence, qui tient tout ensemble. Cela vous permet de revendiquer tous les accesseurs avec un seul appel de méthode, ainsi que de les conserver avec leur instance liée. Une fois que vous avez la référence, vous pouvez passer les accesseurs au code qui en a besoin:
Après beaucoup de coups de tête (pas du bon genre), ce fut ma solution finale, et je l'aime beaucoup. Il est flexible, simple à utiliser et permet un très bon contrôle sur l'accès aux classes. (L' accès avec référence uniquement est très utile.) Si vous utilisez protégé au lieu de privé pour les accesseurs / références, les sous-classes de Foo peuvent même renvoyer des références étendues à partir de
getReference
. Il ne nécessite également aucune réflexion, il peut donc être utilisé dans n'importe quel environnement.la source
Depuis Java 9, des modules peuvent être utilisés pour en faire un problème dans de nombreux cas.
la source
Je préfère la délégation ou la composition ou la classe d'usine (en fonction du problème qui entraîne ce problème) pour éviter d'en faire une classe publique.
S'il s'agit d'un problème de "classes d'interface / d'implémentation dans différents packages", j'utiliserais une classe de fabrique publique qui serait dans le même package que le package impl et empêcherait l'exposition de la classe impl.
Si c'est un problème "Je déteste rendre cette classe / méthode publique juste pour fournir cette fonctionnalité à une autre classe dans un autre package", alors j'utiliserais une classe déléguée publique dans le même package et n'exposerais que cette partie de la fonctionnalité nécessaire par la classe "outsider".
Certaines de ces décisions sont dictées par l'architecture de chargement de classe du serveur cible (bundle OSGi, WAR / EAR, etc.), le déploiement et les conventions de dénomination des packages. Par exemple, la solution proposée ci-dessus, le modèle «Friend Accessor» est intelligente pour les applications java normales. Je me demande s'il est difficile de l'implémenter dans OSGi en raison de la différence de style de chargement de classe.
la source
Je ne sais pas si cela est utile à personne, mais je l'ai traité de la manière suivante:
J'ai créé une interface (AdminRights).
Chaque classe qui devrait pouvoir appeler lesdites fonctions devrait implémenter AdminRights.
J'ai ensuite créé une fonction HasAdminRights comme suit:
la source
J'ai vu une fois une solution basée sur la réflexion qui effectuait une "vérification d'amis" au moment de l'exécution en utilisant la réflexion et la vérification de la pile d'appels pour voir si la classe appelant la méthode était autorisée à le faire. Étant un contrôle d'exécution, il présente l'inconvénient évident.
la source