J'ai un problème de dissimulation de nom qui est extrêmement difficile à résoudre. Voici une version simplifiée qui explique le problème:
Il y a une classe: org.A
package org;
public class A{
public class X{...}
...
protected int net;
}
Ensuite, il y a une classe net.foo.X
package net.foo;
public class X{
public static void doSomething();
}
Et maintenant, voici la classe problématique qui hérite de A
et veut appeler 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
}
}
Comme vous le voyez, ce n'est pas possible. Je ne peux pas utiliser le nom simple X
car il est masqué par un type hérité. Je ne peux pas utiliser le nom qualifié complet net.foo.X
, car net
est masqué par un champ hérité.
Seule la classe B
est dans ma base de code; les classes net.foo.X
et org.A
sont des classes de bibliothèque, je ne peux donc pas les modifier!
Ma seule solution ressemble à ceci: Je pourrais appeler une autre classe qui appelle à son tour X.doSomething()
; mais cette classe n'existerait que par le nom clash, qui semble très compliqué! N'existe-t-il aucune solution permettant d'appeler directement X.doSomething()
à partir de B.doSomething()
?
Dans un langage qui permet de spécifier l'espace de noms global, par exemple global::
en C # ou ::
en C++, je pourrais simplement préfixer net
avec ce préfixe global, mais Java ne le permet pas.
Vous pouvez convertir une variable null
en type, puis invoquer la méthode sur cette méthode (ce qui fonctionnera, car l'objet cible n'intervient pas dans l'appel des méthodes statiques).
((net.foo.X) null).doSomething();
Cela a les avantages de
net.foo.X
),B
le nom que vous souhaitez lui donner; c'est pourquoi un import static
ne fonctionnera pas dans votre cas exact),L'inconvénient est que ce code est vraiment horrible! Pour moi, cela génère un avertissement, et c'est une bonne chose en général. Mais comme il s’agit de résoudre un problème qui est par ailleurs tout à fait irréalisable, ajouter un
@SuppressWarnings("static-access")
à un endroit approprié (minimal!), le compilateur sera fermé.
La manière la plus simple (pas nécessairement la plus facile) de gérer cela serait probablement avec une classe déléguée:
import net.foo.X;
class C {
static void doSomething() {
X.doSomething();
}
}
et alors ...
class B extends A {
void doX(){
C.doSomething();
}
}
C'est un peu verbeux, mais très flexible - vous pouvez le faire se comporter comme vous le souhaitez; De plus, cela fonctionne à peu près de la même manière avec les méthodes static
et les objets instanciés.
Plus d'informations sur les objets délégués ici: http://fr.wikipedia.org/wiki/Delegation_pattern
Vous pouvez utiliser une importation statique:
import static net.foo.X.doSomething;
class B extends A {
void doX(){
doSomething();
}
}
Attention, B
et A
ne contiennent pas de méthodes nommées doSomething
La manière appropriée de faire les choses serait l'importation statique, mais dans le pire des cas, vous POUVEZ construire une instance de la classe en utilisant la réflexion si vous connaissez son nom complet.
Java: newInstance de la classe qui n'a pas de constructeur par défaut
Et ensuite invoquer la méthode sur l'instance.
Ou alors, invoquez simplement la méthode elle-même avec réflexion: Invocation d’une méthode statique à l’aide de réflexion
Class<?> clazz = Class.forName("net.foo.X");
Method method = clazz.getMethod("doSomething");
Object o = method.invoke(null);
Bien sûr, ce sont évidemment les derniers recours.
Pas vraiment LA réponse, mais vous pouvez créer une instance de X et appeler la méthode statique sur celle-ci ..__ Ce serait un moyen (sale, je l’avoue) d’appeler votre méthode.
(new net.foo.X()).doSomething();
Il n'est pas nécessaire de lancer un casting, de supprimer des avertissements étranges ou de créer une instance redondante. Juste une astuce en utilisant le fait que vous pouvez appeler des méthodes statiques de classe parent via la sous-classe. (Similaire à ma solution hackish ici .)
Il suffit de créer une classe comme celle-ci
public final class XX extends X {
private XX(){
}
}
(Le constructeur privé de cette classe finale veille à ce que personne ne puisse créer accidentellement une instance de cette classe.)
Ensuite, vous êtes libre d'appeler X.doSomething()
via celle-ci:
public class B extends A {
public void doSomething() {
XX.doSomething();
}
Et si vous essayez d'obtenir le gobalnamespace étant donné que tous les fichiers sont dans le même dossier. ( 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...
}
}
C'est l'une des raisons pour lesquelles la composition est préférable à l'héritage.
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();
}
}
Je voudrais utiliser le modèle de stratégie.
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();
}
}
Maintenant que vous avez également découplé votre dépendance, vos tests unitaires seront plus faciles à écrire.