web-dev-qa-db-fra.com

Interfaces en Java: impossible de rendre les méthodes implémentées protégées ou privées

Je sais qu'une interface doit être publique. Cependant, je ne veux pas ça.

Je veux que mes méthodes implémentées ne soient accessibles qu'à partir de leur propre package, je veux donc que mes méthodes implémentées soient protégées.

Le problème est que je ne peux pas protéger l'interface ou les méthodes implémentées.

Qu'est-ce qu'un travail autour? Existe-t-il un modèle de conception lié à ce problème?

D'après le guide Java, une classe abstraite ne ferait pas le travail non plus.

25
jbu

lire ceci .

"Le spécificateur d’accès public indique que l’interface peut être utilisée par n’importe quelle classe de n’importe quel paquet. Si vous ne spécifiez pas que l’interface est publique, votre interface ne sera accessible qu’aux classes définies dans le même paquet que l’interface . "

Est-ce que c'est ce que tu veux?

24
duffymo

Votre classe peut utiliser la protection de package tout en implémentant une interface:

class Foo implements Runnable 
{
    public void run()
    {
    }
}

Si vous voulez que certaines méthodes soient protégées/package et que d'autres ne le soient pas, il semble que vos classes ont plus d'une responsabilité et doivent être divisées en plusieurs.

Modifier après avoir lu les commentaires de cette réponse et d’autres:

Si vous pensez en quelque sorte que la visibilité d'une méthode affecte la possibilité d'invoquer cette méthode, détrompez-vous. Sans aller au-delà, vous ne pouvez empêcher quelqu'un d'utiliser la réflexion pour identifier les méthodes de votre classe et les invoquer. Cependant, il ne s'agit pas d'un problème: à moins que quelqu'un ne tente de déchiffrer votre code, il ne va pas invoquer de méthodes aléatoires.

Pensez plutôt aux méthodes privées/protégées comme définissant un contrat pour des sous-classes et utilisez des interfaces pour définir le contrat avec le monde extérieur.

Oh, et à la personne qui a décidé que mon exemple devrait utiliser les entretoises K & R: si cela est spécifié dans les Conditions d'utilisation, bien sûr. Sinon, ne pouvez-vous rien trouver de mieux à faire avec votre temps?

12
kdgregory

Lorsque je me suis heurté à cela, j'utilise une classe interne ou imbriquée accessible par paquet pour implémenter l'interface, poussant la méthode implémentée hors de la classe publique.

D'habitude, c'est parce que j'ai une classe avec une API publique spécifique qui doit implémenter autre chose pour faire son travail (assez souvent parce que l'autre chose était un rappel masqué en tant qu'interface <grin>). Je ne veux pas que l'API publique soit polluée par la mise en œuvre de l'interface (public forcé).

J'espère que cela t'aides.

De même, si vous voulez vraiment que les méthodes ne soient accessibles que par le package, vous ne voulez pas du spécificateur d'étendue protégée, vous voulez le spécificateur d'étendue par défaut (omis). L'utilisation de protected permettra bien sûr aux sous-classes de voir les méthodes.

BTW, je pense que la raison pour laquelle les méthodes d’interface sont supposées être publiques, c’est parce que c’est vraiment l’exception d’avoir une interface qui n’est implémentée que par des classes du même paquet; ils sont très souvent invoqués par quelque chose dans un autre paquet, ce qui signifie qu'ils doivent être publics.

8
Lawrence Dol

Cette question est basée sur une déclaration erronée:

Je sais qu'une interface doit être publique

Pas vraiment, vous pouvez avoir des interfaces avec modificateur d'accès par défaut.

Le problème est que je ne peux pas protéger l'interface ou les méthodes implémentées

C'est ici:

C:\oreyes\cosas\Java\interfaces>type a\*.Java
a\Inter.Java
package a;

interface Inter {
    public void face();
}

a\Face.Java
package a;

class Face implements Inter {
    public void face() {
        System.out.println( "face" );
    }
}


C:\oreyes\cosas\Java\interfaces>type b\*.Java
b\Test.Java

package b;
import a.Inter;
import a.Face;

public class Test {
    public static void main( String [] args ) {
        Inter inter = new Face();
        inter.face();
    }
}

C:\oreyes\cosas\Java\interfaces>javac -d . a\*.Java b\Test.Java
b\Test.Java:2: a.Inter is not public in a; cannot be accessed from outside package
import a.Inter;
        ^
b\Test.Java:3: a.Face is not public in a; cannot be accessed from outside package
import a.Face;
        ^
b\Test.Java:7: cannot find symbol
symbol  : class Inter
location: class b.Test
        Inter inter = new Face();
        ^
b\Test.Java:7: cannot find symbol
symbol  : class Face
location: class b.Test
        Inter inter = new Face();
                          ^
4 errors

C:\oreyes\cosas\Java\interfaces>

Par conséquent, si vous réalisez ce que vous voulez, empêchez l’utilisation de l’interface et des classes en dehors du paquet.

4
OscarRyz

Voici comment cela pourrait être fait en utilisant des classes abstraites.

Le seul inconvénient est que cela vous rend "sous-classe".

Selon le guide Java, vous devriez suivre ce conseil "la plupart" du temps, mais je pense que dans cette situation, tout ira bien.

public abstract class Ab { 
    protected abstract void method();
    abstract void otherMethod();
    public static void main( String [] args ) { 
        Ab a = new AbImpl();
        a.method();
        a.otherMethod();
    }
}
class AbImpl extends Ab {
    protected void method(){
        System.out.println( "method invoked from: " + this.getClass().getName() );
    }
    void otherMethod(){ 
        System.out.println("This time \"default\" access from: " + this.getClass().getName()  );
    }
}
3
OscarRyz

Voici une autre solution, inspirée du langage C++ Pimpl.

Si vous souhaitez implémenter une interface, mais ne souhaitez pas que cette implémentation soit publique, vous pouvez créer un objet composé d'une classe interne anonyme qui implémente l'interface.

Voici un exemple. Disons que vous avez cette interface:

public interface Iface {
    public void doSomething();
}

Vous créez un objet de type Iface et placez votre implémentation dans cet emplacement:

public class IfaceUser {
    private int someValue;
    // Here's our implementor
    private Iface impl = new Iface() {
        public void doSomething() {
            someValue++;
        }
    };
}

Chaque fois que vous avez besoin d'appeler doSomething(), vous l'invoquez sur votre objet impl composé.

1
Karl Giesing

Je viens de découvrir ceci en essayant de construire une méthode protégée avec l'intention de l'utiliser uniquement dans un scénario de test. Je voulais supprimer les données de test que j'avais bourrées dans une table de base de données. En tout cas, je me suis inspiré de -Karl Giesing 's post . Malheureusement cela n'a pas fonctionné. J'ai trouvé un moyen de le faire fonctionner en utilisant une classe interne protégée.

L'interface:

package foo;
interface SomeProtectedFoo {
    int doSomeFoo();
}

Ensuite, la classe interne définie comme protégée dans la classe publique:

package foo;
public class MyFoo implements SomePublicFoo {
    // public stuff
    protected class ProtectedFoo implements SomeProtectedFoo {
        public int doSomeFoo() { ... }
    }
    protected ProtectedFoo pFoo;
    protected ProtectedFoo gimmeFoo() {
        return new ProtectedFoo();
    }
}

Vous pouvez alors accéder à la méthode protégée uniquement à partir d'autres classes du même package, car mon code de test était le suivant:

package foo;
public class FooTest {
    MyFoo myFoo = new MyFoo();
    void doProtectedFoo() {
        myFoo.pFoo = myFoo.gimmeFoo();
        myFoo.pFoo.doSomeFoo();
    }
}

Un peu tard pour l'affiche originale, mais bon, je viens de le trouver. :RÉ

1
siliconsmiley

Avec une interface, vous souhaitez définir des méthodes pouvant être exposées par diverses classes d’implémentation. Avoir une interface avec des méthodes protégées ne servirait tout simplement pas cet objectif.

Je devine que votre problème peut être résolu en redessinant votre hiérarchie de classe.

0
Jonas Eicher

Je pense que vous pouvez l’utiliser maintenant avec la version Java 9. À partir des notes openJdk pour Java 9,

La prise en charge des méthodes privées dans les interfaces a été brièvement envisagée pour l’inclusion dans Java SE 8 dans le cadre de l’effort visant à ajouter la prise en charge des expressions Lambda, mais a été retirée pour permettre de mieux se concentrer sur les tâches prioritaires pour Java SE 8. pour les méthodes d’interface privée, permettant ainsi aux méthodes non abstraites d’une interface de partager du code entre elles.

se référer https://bugs.openjdk.Java.net/browse/JDK-8071453

0
Mounika Sai

Une façon de contourner ce problème consiste (selon la situation) à créer une classe interne anonyme qui implémente l'interface de portée protected ou private. Par exemple:

public class Foo {
    interface Callback {
        void hiddenMethod();
    }

    public Foo(Callback callback) {
    }
}

Puis dans l'utilisateur de Foo:

public class Bar {
    private Foo.Callback callback = new Foo.Callback() { 
        @Override public void hiddenMethod() { ... }
    };
    private Foo foo = new Foo(callback);
}

Cela vous évite d'avoir les éléments suivants:

public class Bar implements Foo.Callback {
    private Foo foo = new Foo(this);

    // uh-oh! the method is public!
    @Override public void hiddenMethod() { ... }
}
0
Jin

Vous pouvez choisir l’encapsulation au lieu de l’héritage.

C'est-à-dire, créez votre classe (qui n'héritera de rien) et dans laquelle vous aurez une instance de l'objet que vous souhaitez étendre.

Ensuite, vous pouvez exposer uniquement ce que vous voulez.

L'inconvénient évident de cela est que vous devez explicitement passer par des méthodes pour tout ce que vous voulez exposer. Et ce ne sera pas une sous-classe ...

0
Michael Haren

Je voudrais juste créer une classe abstraite. Il n'y a pas de mal à cela.

0
Hash