web-dev-qa-db-ja.com

Java:サブパッケージの可視性?

プロジェクトに2つのパッケージがあります:odp.projおよびodp.proj.test。これら2つのパッケージのクラスにのみ表示したい特定のメソッドがあります。これどうやってするの?

編集: Javaにサブパッケージの概念がない場合、これを回避する方法はありますか?テスターとそのパッケージの他のメンバーだけが利用できるようにする特定のメソッドがあります。すべてを同じパッケージに入れる必要がありますか?広範な反射を使用しますか?

136
Nick Heiner

できません。 Javaではサブパッケージの概念がないため、odp.projとodp.proj.testは完全に別個のパッケージです。

152
starblue

パッケージの名前は、ここでのアプリケーションが単体テスト用であることを示唆しています。使用される典型的なパターンは、テストするクラスとユニットテストコードを同じパッケージ(場合によってはodp.proj)に、ただし異なるソースツリーに配置することです。したがって、クラスをsrc/odp/projに、テストコードをtest/odp/projに配置します。

Javaには、「パッケージ」アクセス修飾子があります。これは、何も指定されていない場合(つまり、public、private、またはprotectedを指定しない場合)のデフォルトのアクセス修飾子です。 「パッケージ」アクセス修飾子を使用すると、odp.projのクラスのみがメソッドにアクセスできます。ただし、Javaでは、リフレクションを使用するとすべてのアクセスが可能になるため、アクセスルールを適用するためにアクセス修飾子を使用することはできません。アクセス修飾子は示唆的なものにすぎません(制限的なセキュリティマネージャーが存在しない場合)。

57
Asaph

これはodp.projodp.proj.testの間の特別な関係ではありません-それらはたまたま明らかに関連していると命名されています。

Odp.proj.testパッケージが単にテストを提供している場合、同じパッケージ名(odp.proj)を使用できます。 EclipseやNetbeansなどのIDEは、同じパッケージ名でJUnitセマンティクスを使用して、個別のフォルダー(src/main/Java/odp/projおよびsrc/test/Java/odp/proj)を作成します。

これらのIDEはodp.projのメソッドのテストを生成し、存在しないテストメソッドに適切なフォルダーを作成することに注意してください。

11

IntelliJでこれを行うと、ソースツリーは次のようになります。

src         // source root
- odp
   - proj   // .Java source here
- test      // test root
  - odp
     - proj // JUnit or TestNG source here
5
duffymo

編集:Javaにサブパッケージの概念がない場合、これを回避する方法はありますか?テスターとそのパッケージの他のメンバーだけが利用できるようにする特定のメソッドがあります。

それらを表示しない動機におそらく依存しますが、唯一の理由が、パブリックインターフェイスをテストのみを目的とするもの(または他の内部のもの)で汚染したくない場合である場合、メソッドを個別のパブリックインターフェイスを作成し、「非表示」メソッドのコンシューマーにそのインターフェイスを使用させます。他の人がインターフェースを使用するのを止めることはありませんが、なぜそうすべきなのか分かりません。

単体テストの場合、およびロットを書き換えなくても可能であれば、同じパッケージを使用するための提案に従ってください。

4
Fredrik

他の人が説明したように、Javaには「サブパッケージ」のようなものはありません。すべてのパッケージは分離され、親から何も継承しません。

別のパッケージから保護されたクラスメンバーにアクセスする簡単な方法は、クラスを拡張し、メンバーをオーバーライドすることです。

たとえば、パッケージa.bClassInAにアクセスするには:

package a;

public class ClassInA{
    private final String data;

    public ClassInA(String data){ this.data = data; }

    public String getData(){ return data; }

    protected byte[] getDataAsBytes(){ return data.getBytes(); }

    protected char[] getDataAsChars(){ return data.toCharArray(); }
}

ClassInAで必要なメソッドをオーバーライドするクラスをそのパッケージに作成します。

package a.b;

import a.ClassInA;

public class ClassInAInB extends ClassInA{
    ClassInAInB(String data){ super(data); }

    @Override
    protected byte[] getDataAsBytes(){ return super.getDataAsBytes(); }
}

これにより、他のパッケージのクラスの代わりにオーバーライドクラスを使用できます。

package a.b;

import Java.util.Arrays;

import a.ClassInA;

public class Driver{
    public static void main(String[] args){
        ClassInA classInA = new ClassInA("string");
        System.out.println(classInA.getData());
        // Will fail: getDataAsBytes() has protected access in a.ClassInA
        System.out.println(Arrays.toString(classInA.getDataAsBytes()));

        ClassInAInB classInAInB = new ClassInAInB("string");
        System.out.println(classInAInB.getData());
        // Works: getDataAsBytes() is now accessible
        System.out.println(Arrays.toString(classInAInB.getDataAsBytes()));
    }
}

これは、拡張クラス(継承)に表示される保護されたメンバーに対してのみ機能し、同じパッケージ内のサブ/拡張クラスにのみ表示されるパッケージプライベートメンバーには機能しないことに注意してください。これが誰かの助けになることを願っています!

2
ndm13

PackageVisibleHelperクラスを使用し、PackageVisibleHelperFactoryを凍結する前にプライベートにしておくと、launchA(by PackageVisibleHelper)メソッドをどこでも呼び出すことができます:)

package odp.proj;
public class A
 {
    void launchA() { }
}

public class PackageVisibleHelper {

    private final PackageVisibleHelperFactory factory;

    public PackageVisibleHelper(PackageVisibleHelperFactory factory) {
        super();
        this.factory = factory;
    }

    public void launchA(A a) {
        if (factory == PackageVisibleHelperFactory.INSTNACNE && !factory.isSampleHelper(this)) {
            throw new IllegalAccessError("wrong PackageVisibleHelper ");
        }
        a.launchA();
    }
}


public class PackageVisibleHelperFactory {

    public static final PackageVisibleHelperFactory INSTNACNE = new PackageVisibleHelperFactory();

    private static final PackageVisibleHelper HELPER = new PackageVisibleHelper(INSTNACNE);

    private PackageVisibleHelperFactory() {
        super();
    }

    private boolean frozened;

    public PackageVisibleHelper getHelperBeforeFrozen() {
        if (frozened) {
            throw new IllegalAccessError("please invoke before frozen!");
        }
        return HELPER;
    }

    public void frozen() {
        frozened = true;
    }

    public boolean isSampleHelper(PackageVisibleHelper helper) {
        return HELPER.equals(helper);
    }
}
package odp.proj.test;

import odp.proj.A;
import odp.proj.PackageVisibleHelper;
import odp.proj.PackageVisibleHelperFactory;

public class Test {

    public static void main(String[] args) {

        final PackageVisibleHelper helper = PackageVisibleHelperFactory.INSTNACNE.getHelperBeforeFrozen();
        PackageVisibleHelperFactory.INSTNACNE.frozen();


        A a = new A();
        helper.launchA(a);

        // illegal access       
        new PackageVisibleHelper(PackageVisibleHelperFactory.INSTNACNE).launchA(a); 
    }
}
0
qxo

メソッドの前にアクセス修飾子を配置せずに、それがパッケージプライベートであると言います。
次の例をご覧ください。

package odp.proj;
public class A
{
    void launchA() { }
}

package odp.proj.test;
public class B
{
    void launchB() { }
}

public class Test
{
    public void test()
    {
        A a = new A();
        a.launchA()    // cannot call launchA because it is not visible
    }
}
0