web-dev-qa-db-fra.com

Cacher le nom de Java: La manière dure

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.

96
gexicide

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

  • être libre d'effets secondaires (un problème d'instanciation net.foo.X),
  • ne nécessitant pas de renommer quoi que ce soit (vous pouvez donc donner à la méthode dans B le nom que vous souhaitez lui donner; c'est pourquoi un import static ne fonctionnera pas dans votre cas exact),
  • ne nécessitant pas l'introduction de classe de délégués (bien que cela puisse être une bonne idée…), et
  • ne nécessitant pas la surcharge ou la complexité de l'utilisation de l'API de réflexion.

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é.

84
Donal Fellows

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

38
blgt

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

37
Absurd-Mind

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.

16
EpicPandaForce

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();
6
Michael Laffargue

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();
        }
4
billc.cn

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...

         }
     }
2
Vectoria

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

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.

0
Erik Madsen