it-swarm.com.de

Schnittstellen in Java: Implementierte Methoden können nicht geschützt oder privat gemacht werden

Ich weiß, dass eine Schnittstelle öffentlich sein muss. Das will ich aber nicht.

Ich möchte, dass meine implementierten Methoden nur von ihrem eigenen Paket aus zugänglich sind, also möchte ich, dass meine implementierten Methoden geschützt sind.

Das Problem ist, dass ich die Schnittstelle oder die implementierten Methoden nicht schützen kann.

Was ist ein Workaround? Gibt es ein Entwurfsmuster, das sich auf dieses Problem bezieht?

Aus dem Java-Handbuch geht hervor, dass auch eine abstrakte Klasse diese Aufgabe nicht erfüllt.

25
jbu

lies this .

Msgstr "Der Public Access - Bezeichner gibt an, dass die Schnittstelle von jeder Klasse in jedem Paket verwendet werden kann. Wenn Sie nicht angeben, dass die Schnittstelle öffentlich ist, ist Ihre Schnittstelle nur für Klassen zugänglich, die in demselben Paket wie die Schnittstelle definiert sind. . "

Ist es das was du willst?

24
duffymo

Ihre Klasse kann den Paketschutz verwenden und dennoch eine Schnittstelle implementieren:

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

Wenn Sie möchten, dass einige Methoden geschützt/gepackt werden und andere nicht, haben Ihre Klassen offenbar mehr als eine Verantwortung und sollten in mehrere aufgeteilt werden.

Bearbeiten Sie nach dem Lesen von Kommentaren zu diesen und anderen Antworten:

Wenn Sie der Meinung sind, dass sich die Sichtbarkeit einer Methode auf die Fähigkeit auswirkt, diese Methode aufzurufen, denken Sie erneut. Sie können niemanden daran hindern, mithilfe von Reflektion die Methoden Ihrer Klasse zu identifizieren und sie aufzurufen, ohne zu extremen Bedingungen zu gehen. Dies ist jedoch kein Problem: Wenn nicht jemand versucht, Ihren Code zu knacken, wird er keine zufälligen Methoden aufrufen.

Stellen Sie sich stattdessen private/protected-Methoden als Definition eines Vertrags für Unterklassen vor und verwenden Sie Schnittstellen, um den Vertrag mit der Außenwelt zu definieren.

Oh, und zu der Person, die mein Beispiel ausgewählt hat, sollte K & R-Verstrebungen verwenden: Wenn dies in den Nutzungsbedingungen angegeben ist, sicher. Können Sie sonst nichts Besseres für Ihre Zeit finden?

12
kdgregory

Wenn ich dagegen angekommen bin, verwende ich eine innere oder verschachtelte Klasse, auf die über ein Paket zugegriffen werden kann, um die Schnittstelle zu implementieren, und schiebe die implementierte Methode aus der öffentlichen Klasse heraus.

Normalerweise liegt es daran, dass ich eine Klasse mit einer bestimmten öffentlichen API habe, die etwas anderes implementieren muss, um ihre Arbeit zu erledigen (ziemlich oft, weil das andere ein als Schnittstelle getarnter Rückruf war <grin>) - dies passiert häufig mit Dingen wie Comparable. Ich möchte nicht, dass die öffentliche API durch die Implementierung der (erzwungenen öffentlichen) Schnittstelle verschmutzt wird.

Hoffe das hilft.

Wenn Sie wirklich möchten, dass nur über das Paket auf die Methoden zugegriffen wird, möchten Sie nicht den geschützten Bereichsbezeichner, sondern den standardmäßigen (ausgelassenen) Bereichsbezeichner. Durch die Verwendung von protected können Unterklassen natürlich die Methoden anzeigen.

Übrigens glaube ich, dass der Grund dafür, dass Schnittstellenmethoden als öffentlich bezeichnet werden, darin besteht, dass eine Schnittstelle, die nur von Klassen im selben Paket implementiert wird, nur in Ausnahmefällen verwendet wird. Sie werden sehr oft von etwas in einem anderen Paket aufgerufen, was bedeutet, dass sie öffentlich sein müssen.

8
Lawrence Dol

Diese Frage basiert auf einer falschen Aussage:

Ich weiß, dass eine Schnittstelle öffentlich sein muss

Nicht wirklich, Sie können Schnittstellen mit Standardzugriffsmodifikator haben.

Das Problem ist, ich kann nicht die Schnittstelle oder die implementierten Methoden geschützt machen

Hier ist es:

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>

Vermeiden Sie daher die Verwendung von Schnittstellen und Klassen außerhalb des Pakets, um das Gewünschte zu erreichen.

4
OscarRyz

Hier erfahren Sie, wie Sie abstrakte Klassen verwenden können.

Das einzig Unbequeme ist, dass Sie dadurch zu einer "Unterklasse" werden.

Gemäß dem Java-Handbuch sollten Sie diesen Rat "meistens" befolgen, aber ich denke, in dieser Situation wird es in Ordnung sein.

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

Hier ist eine andere Lösung, die von der C++ - Pimpl-Sprache inspiriert ist.

Wenn Sie eine Schnittstelle implementieren möchten, diese Implementierung jedoch nicht öffentlich sein soll, können Sie ein zusammengesetztes Objekt einer anonymen inneren Klasse erstellen, die die Schnittstelle implementiert.

Hier ist ein Beispiel. Angenommen, Sie haben diese Schnittstelle:

public interface Iface {
    public void doSomething();
}

Sie erstellen ein Objekt vom Typ Iface und platzieren Ihre Implementierung dort:

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

Wann immer Sie doSomething() aufrufen müssen, rufen Sie es für Ihr zusammengesetztes impl-Objekt auf.

1
Karl Giesing

Ich bin gerade auf diesen Versuch gestoßen, eine geschützte Methode zu erstellen, mit der Absicht, dass sie nur in einem Testfall verwendet wird. Ich wollte Testdaten löschen, die ich in eine DB-Tabelle gestopft hatte. Auf jeden Fall hat mich @Karl Giesing s post inspiriert. Leider hat es nicht geklappt. Ich habe mir einen Weg ausgedacht, wie ich es mit einer geschützten inneren Klasse zum Laufen bringen kann.

Die Schnittstelle:

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

Dann wird die innere Klasse als in der öffentlichen Klasse geschützt definiert:

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

Sie können dann nur von anderen Klassen im selben Paket auf die geschützte Methode zugreifen, da mein Testcode wie folgt lautete:

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

Ein bisschen zu spät für das Originalplakat, aber hey, ich habe es gerade gefunden. : D

1
siliconsmiley

Mit einer Schnittstelle möchten Sie Methoden definieren, die von verschiedenen implementierenden Klassen verfügbar gemacht werden können. Eine Schnittstelle mit geschützten Methoden zu haben, würde diesen Zweck einfach nicht erfüllen.

Ich nehme an, dass Ihr Problem gelöst werden kann, indem Sie Ihre Klassenhierarchie neu gestalten.

0
Jonas Eicher

Ich denke, Sie können es jetzt mit Java 9 Release verwenden. In den openJdk-Hinweisen für Java 9 heißt es:

Die Unterstützung für private Methoden in Schnittstellen wurde kurzzeitig in Betracht gezogen, um sie in Java SE 8 aufzunehmen, um die Unterstützung für Lambda Expressions hinzuzufügen. Sie wurde jedoch zurückgezogen, um eine bessere Fokussierung auf Aufgaben mit höherer Priorität für Java SE 8 zu ermöglichen für private Schnittstellen werden Methoden durchgeführt, die es nicht abstrakten Methoden einer Schnittstelle ermöglichen, Code zwischen ihnen zu teilen.

refer https://bugs.openjdk.Java.net/browse/JDK-8071453

0
Mounika Sai

Eine Möglichkeit, dies zu umgehen, besteht (je nach Situation) darin, eine anonyme innere Klasse zu erstellen, die die Schnittstelle mit dem Gültigkeitsbereich protected oder private implementiert. Zum Beispiel:

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

    public Foo(Callback callback) {
    }
}

Dann im Benutzer von Foo:

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

Dies erspart Ihnen Folgendes:

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

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

Sie können die Kapselung anstelle der Vererbung verwenden.

Das heißt, erstellen Sie Ihre Klasse (die nichts erbt) und enthalten Sie eine Instanz des Objekts, das Sie erweitern möchten.

Dann können Sie nur das aussetzen, was Sie wollen.

Der offensichtliche Nachteil dabei ist, dass Sie Methoden für alles, was Sie verfügbar machen möchten, explizit durchlaufen müssen. Und es wird keine Unterklasse sein ...

0
Michael Haren

Ich würde nur eine abstrakte Klasse erstellen. Darin liegt kein Schaden.

0
Hash