プロジェクトに2つのパッケージがあります:odp.proj
とodp.proj.test
。これらの2つのパッケージのクラスだけに表示したい特定のメソッドがあります。これどうやってするの?
編集: Javaにサブパッケージの概念がない場合、これを回避する方法はありますか?テスターやそのパッケージの他のメンバーだけが使用できるようにする特定のメソッドがあります。私はすべてを同じパッケージに入れるべきですか?広範な反射を使用しますか?
プロジェクトに2つのパッケージがあります:odp.proj
とodp.proj.test
。これらの2つのパッケージのクラスだけに表示したい特定のメソッドがあります。これどうやってするの?
編集: Javaにサブパッケージの概念がない場合、これを回避する方法はありますか?テスターやそのパッケージの他のメンバーだけが使用できるようにする特定のメソッドがあります。私はすべてを同じパッケージに入れるべきですか?広範な反射を使用しますか?
回答:
できません。Javaにはサブパッケージの概念がないためodp.proj
、odp.proj.test
完全に別のパッケージです。
パッケージの名前は、ここでのアプリケーションが単体テスト用であることを示唆しています。使用される典型的なパターンは、テストしたいクラスとユニットテストコードを同じパッケージ(あなたの場合odp.proj
)に、異なるソースツリーに置くことです。したがって、クラスはsrc/odp/proj
に、テストコードはに配置しtest/odp/proj
ます。
Javaには「パッケージ」アクセス修飾子があります。これは、何も指定されていない場合(つまり、パブリック、プライベート、または保護を指定していない場合)のデフォルトのアクセス修飾子です。「パッケージ」アクセス修飾子odp.proj
を使用すると、のクラスのみがメソッドにアクセスできます。ただし、Javaでは、リフレクションを使用して任意のアクセスが可能であるため、アクセス修飾子を使用してアクセスルールを適用することはできません。アクセス修飾子は単に示唆的なものです(制限的なセキュリティマネージャーが存在しない限り)。
これは、間に特別な関係はありませんodp.proj
し、odp.proj.test
彼らは明らかに関連して命名することが起こります- 。
odp.proj.testパッケージが単にテストを提供している場合は、同じパッケージ名(odp.proj
)を使用できます。EclipseやNetbeansのようなIDE は、同じパッケージ名でJUnitセマンティクスを持つ別個のフォルダー(src/main/java/odp/proj
およびsrc/test/java/odp/proj
)を作成します。
これらのIDEはメソッドのテストを生成odp.proj
し、存在しないテストメソッド用の適切なフォルダーを作成することに注意してください。
編集:Javaにサブパッケージの概念がない場合、これを回避する方法はありますか?テスターやそのパッケージの他のメンバーだけが使用できるようにする特定のメソッドがあります。
おそらくそれらを表示しない動機に少し依存しますが、唯一の理由が、テストのみを目的としたもの(またはその他の内部的なもの)でパブリックインターフェイスを汚染したくない場合は、メソッドを別個のパブリックインターフェイスを使用して、「非表示」メソッドのコンシューマーにそのインターフェイスを使用させる。他の人がインターフェースを使用するのを止めることはありませんが、あなたがそうすべき理由は私にはわかりません。
単体テストの場合、ロットを書き直さなくても可能であれば、提案に従って同じパッケージを使用してください。
他の人が説明したように、Javaには「サブパッケージ」などはありません。すべてのパッケージは分離され、親から何も継承しません。
別のパッケージから保護されたクラスメンバーにアクセスする簡単な方法は、クラスを拡張してメンバーをオーバーライドすることです。
たとえばClassInA
、パッケージでアクセスするにはa.b
:
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()));
}
}
これは、拡張クラス(継承)に表示される保護されたメンバーに対してのみ機能し、同じパッケージ内のサブ/拡張クラスにのみ表示されるパッケージプライベートメンバーには機能しないことに注意してください。うまくいけば、これは誰かを助けるでしょう!
ここでの回答のほとんどは、Javaにはサブパッケージなどがないと述べていますが、厳密には正確ではありません。この用語は、Java言語仕様に含まれており、Java 6までさかのぼります(おそらく以前のバージョンのJava用のJLSの自由にアクセスできるバージョンはないようです)。サブパッケージを取り巻く言語は、Java 6以降のJLSではあまり変わっていません。
パッケージのメンバーは、そのサブパッケージと、パッケージのすべてのコンパイルユニットで宣言されているすべてのトップレベルのクラスタイプとトップレベルのインターフェイスタイプです。
たとえば、Java SEプラットフォームAPIでは次のようになります。
- パッケージには、
java
サブパッケージがあるawt
、applet
、io
、lang
、net
、とutil
、ないコンパイル単位を。- パッケージに
java.awt
は、という名前のサブパッケージとimage
、クラスおよびインターフェイスタイプの宣言を含むいくつかのコンパイル単位があります。
サブパッケージの概念は、パッケージとクラス/インターフェース間の命名の制約を適用するので、関連しています。
パッケージには同じ名前の2つのメンバーが含まれていない場合があり、そうでない場合はコンパイル時エラーが発生します。
ここではいくつかの例を示します。
- パッケージ
java.awt
にはサブパッケージがあるため、image
という名前のクラスまたはインターフェイス型の宣言を含めることはできません(含めません)image
。- 名前が付けられたパッケージとそのパッケージ内の
mouse
メンバータイプButton
(その後、と呼ばれるmouse.Button
場合があります)がある場合、完全修飾名mouse.Button
またはのパッケージは存在できませんmouse.Button.Click
。- 場合
com.nighthacks.java.jag
型の完全修飾名がある場合、その完全修飾名のいずれかである任意のパッケージがあることはできませんcom.nighthacks.java.jag
かcom.nighthacks.java.jag.scrabble
。
ただし、この命名の制限は、言語によってサブパッケージに与えられる唯一の重要性です。
パッケージの階層的な命名構造は、従来の方法で関連するパッケージを整理するのに便利であることを意図していますが、そのパッケージで宣言された最上位タイプと同じ単純な名前のサブパッケージを持つパッケージに対する禁止以外、それ自体には意味がありません。
たとえば、という名前のパッケージの間には特別なアクセスの関係が存在しない
oliver
とという名前の別のパッケージoliver.twist
、または指定したパッケージの間evelyn.wood
とはevelyn.waugh
。つまり、namedoliver.twist
パッケージのoliver
コードは、他のパッケージのコードよりも、パッケージ内で宣言された型にアクセスできません。
この文脈で、私たちは質問自体に答えることができます。パッケージとそのサブパッケージの間、または親パッケージの2つの異なるサブパッケージの間に特別なアクセス関係がないので、要求された方法でメソッドを2つの異なるパッケージから見えるようにする方法はありません。これは文書化された、意図的な設計上の決定です。
メソッドをパブリックにして、すべてのパッケージ( odp.proj
およびodp.proj.test
)が特定のメソッドにアクセスできるようにするか、メソッドをパッケージプライベート(デフォルトの可視性)にして、直接アクセスする必要のあるすべてのコードを入力する必要があります。メソッドと同じ(サブ)パッケージ。
つまり、Javaでの非常に標準的な慣行は、テストコードをソースコードと同じパッケージに、ファイルシステムの別の場所に配置することです。例えば、でMavenのビルドツール、条約はこれらのソースとテストファイルを置くことであろうsrc/main/java/odp/proj
と
src/test/java/odp/proj
、それぞれ。ビルドツールがこれをコンパイルすると、両方のファイルセットがodp.proj
パッケージにsrc
含まれますが、プロダクションアーティファクトにはファイルのみが含まれます。テストファイルは、本番ファイルを検証するためにビルド時にのみ使用されます。このセットアップにより、テストコードは同じパッケージ内にあるため、テストするコードの任意のパッケージプライベートコードまたは保護コードに自由にアクセスできます。
サブパッケージ間または兄弟パッケージ間でコードを共有したいが、テストや本番のケースではない場合、一部のライブラリで使用されているソリューションの1つは、その共有コードを公開することですが、内部ライブラリ向けであることを文書化します使用のみ。
メソッドの前にアクセス修飾子を置かずに、それはパッケージプライベートであると言います。
次の例を見てください。
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
}
}
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);
}
}