web-dev-qa-db-ja.com

Javaのインターフェース:実装されたメソッドを保護またはプライベートにすることはできません

インターフェイスはパブリックでなければならないことを私は知っています。しかし、私はそれを望んでいません。

実装されたメソッドに独自のパッケージからのみアクセスできるようにしたいので、実装されたメソッドを保護したいと思います。

問題は、インターフェースまたは実装されたメソッドを保護できないことです。

回避策とは何ですか?この問題に関連するデザインパターンはありますか?

Javaガイドから、抽象クラスもその仕事をしません。

27
jbu

読む this

「パブリックアクセス指定子は、インターフェイスが任意のパッケージの任意のクラスで使用できることを示します。インターフェイスがパブリックであることを指定しない場合、インターフェイスはで定義されたクラスにのみアクセスできます。インターフェイスと同じパッケージ。」

それはあなたが望むものですか?

24
duffymo

クラスはパッケージ保護を使用しても、インターフェースを実装できます。

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

一部のメソッドを保護/パッケージ化し、他のメソッドを保護したくない場合は、クラスに複数の責任があるように思われるため、複数に分割する必要があります。

これと他の応答へのコメントを読んだ後に編集してください:

メソッドの可視性がそのメソッドを呼び出す機能に影響を与えると何らかの理由で考えている場合は、もう一度考えてみてください。極端に行かなければ、誰かがリフレクションを使用してクラスのメソッドを識別し、それらを呼び出すのを防ぐことはできません。ただし、これは問題ではありません。誰かがコードを解読しようとしない限り、ランダムなメソッドを呼び出すことはありません。

代わりに、プライベート/保護されたメソッドをサブクラスのコントラクトを定義するものと考え、インターフェイスを使用して外部とのコントラクトを定義します。

ああ、そして私の例を決定した人には、K&Rブレースを使用する必要があります:それが利用規約で指定されている場合は、確かに。そうでなければ、あなたはあなたの時間ともっと良いことを見つけることができませんか?

11
kdgregory

これに突き当たったら、パッケージにアクセス可能な内部クラスまたはネストされたクラスを使用してインターフェイスを実装し、実装されたメソッドをパブリッククラスからプッシュします。

通常、それは、特定のパブリックAPIを備えたクラスがあり、それを実行するために他の何かを実装する必要があるためです(他の何かが、インターフェイス<grin>を装ったコールバックであることがよくあります)。これは、Comparableなどでよく発生します。パブリックAPIが(強制パブリック)インターフェースの実装で汚染されたくありません。

お役に立てれば。

また、パッケージからのみメソッドにアクセスする必要がある場合は、保護されたスコープ指定子は必要ありません。デフォルトの(省略された)スコープ指定子が必要です。もちろん、protected willを使用すると、サブクラスがメソッドを表示できるようになります。

ところで、インターフェイスメソッドがパブリックであると推測される理由は、同じパッケージ内のクラスによってのみ実装されるインターフェイスを持つことは非常に例外であるためだと思います。ほとんどの場合、別のパッケージの何かによって呼び出されます。つまり、公開する必要があります。

8
Lawrence Dol

この質問は間違ったステートメントに基づいています:

インターフェースはパブリックでなければならないことを知っています

実際には、デフォルトのアクセス修飾子を使用してインターフェイスを使用できます。

問題は、インターフェイスまたは実装されたメソッドを保護できないことです

ここにあります:

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>

したがって、必要なことを達成し、パッケージ外でのインターフェースとクラスの使用を防ぎます。

4
OscarRyz

抽象クラスを使用してこれを行う方法は次のとおりです。

唯一の不便な点は、それがあなたを「サブクラス」にすることです。

Javaガイドによると、ほとんどの場合、そのアドバイスに従う必要がありますが、この状況では問題ないと思います。

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

これは、C++ Pimplイディオムに触発された別のソリューションです。

インターフェイスを実装したいが、その実装をパブリックにしたくない場合は、インターフェイスを実装する匿名内部クラスの合成オブジェクトを作成できます。

これが例です。あなたがこのインターフェースを持っているとしましょう:

_public interface Iface {
    public void doSomething();
}
_

Ifaceタイプのオブジェクトを作成し、そこに実装を配置します。

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

doSomething()を呼び出す必要があるときはいつでも、作成したimplオブジェクトで呼び出します。

1
Karl Giesing

テストケースでのみ使用することを意図して、保護されたメソッドを構築しようとしているところに出くわしました。 DBテーブルに詰め込んだテストデータを削除したかったのです。いずれにせよ、私は @ Karl Giesing 's post に触発されました。残念ながら、それは機能しませんでした。保護された内部クラスを使用してそれを機能させる方法を考えました。

インターフェース:

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

次に、パブリッククラスで保護されていると定義された内部クラス:

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

私のテストコードは次のようになっているので、同じパッケージ内の他のクラスからのみ保護されたメソッドにアクセスできます。

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

元のポスターには少し遅れましたが、ちょっと、見つけました。 :D

1
siliconsmiley

インターフェースを使用して、さまざまな実装クラスによって公開できるメソッドを定義する必要があります。保護されたメソッドとのインターフェースを持つことは、その目的を果たしません。

クラス階層を再設計することで問題を解決できると思います。

0
Jonas Eicher

Java 9リリースで使用できるようになったと思います。Java 9、

インターフェイスでのプライベートメソッドのサポートは、Lambda式のサポートを追加する取り組みの一環としてJava SE 8に含めることを簡単に検討していましたが、=の優先度の高いタスクにより集中できるようにするために廃止されました。 Java SE 8.プライベートインターフェイスメソッドのサポートを実施することで、インターフェイスの非抽象メソッドがそれらの間でコードを共有できるようにすることが提案されています。

参照 https://bugs.openjdk.Java.net/browse/JDK-807145

0
Mounika Sai

これを回避する1つの方法は、(状況に応じて)protectedまたはprivateスコープを持つインターフェイスを実装する匿名の内部クラスを作成することです。例えば:

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

    public Foo(Callback callback) {
    }
}

次に、Fooのユーザーで:

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

これにより、次のことを回避できます。

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

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

継承の代わりにカプセル化を使用できます。

つまり、クラス(何も継承しない)を作成し、その中に拡張するオブジェクトのインスタンスを作成します。

次に、必要なものだけを公開できます。

これの明らかな欠点は、公開したいすべてのものに対して明示的にパススルーメソッドを使用する必要があることです。そして、それはサブクラスにはなりません...

0
Michael Haren

抽象クラスを作成するだけです。害はありません。

0
Hash