我的项目中有两个包:odp.proj
和odp.proj.test
. 有些方法我只想对这两个包中的类可见。我怎样才能做到这一点?
编辑:如果 Java 中没有子包的概念,有没有办法解决这个问题?我有某些方法,我希望只对测试人员和该包的其他成员可用。我应该把所有东西都扔进同一个包里吗?使用广泛的反射?
我的项目中有两个包:odp.proj
和odp.proj.test
. 有些方法我只想对这两个包中的类可见。我怎样才能做到这一点?
编辑:如果 Java 中没有子包的概念,有没有办法解决这个问题?我有某些方法,我希望只对测试人员和该包的其他成员可用。我应该把所有东西都扔进同一个包里吗?使用广泛的反射?
包的名称暗示这里的应用程序用于单元测试。使用的典型模式是将您希望测试的类和单元测试代码放在同一个包中(在您的情况下odp.proj
),但放在不同的源代码树中。所以你会把你的类src/odp/proj
放在test/odp/proj
.
Java 确实有“包”访问修饰符,当没有指定时,它是默认的访问修饰符(即您没有指定 public、private 或 protected)。使用“包”访问修饰符,只有其中的类odp.proj
才能访问方法。但请记住,在 Java 中,不能依赖访问修饰符来强制执行访问规则,因为通过反射,任何访问都是可能的。访问修饰符只是提示性的(除非存在限制性安全管理器)。
这里的大多数答案都说明Java中没有子包这样的东西,但这并不准确。该术语在Java 语言规范(JLS) 中定义,并且自该规范的初始版本以来一直存在。
包的成员是它的子包以及包的所有编译单元中声明的所有顶级类类型和顶级接口类型。
例如,在 Java SE Platform API 中:
- 封装
java
有子包awt
,applet
,io
,lang
,net
,和util
,但没有编译单元。- 该包
java.awt
有一个名为 的子包image
,以及许多包含类和接口类型声明的编译单元。
子包概念具有实际意义,因为它强制执行包和类/接口之间的命名约束:
一个包可能不包含两个同名成员,否则会导致编译时错误。
这里有些例子:
- 因为包
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
。
但是,这种命名限制是语言赋予子包的唯一意义:
包的分层命名结构旨在方便以常规方式组织相关包,但除了禁止包具有与该包中声明的顶级类型具有相同简单名称的子包外,其本身没有任何意义.
例如,named 包
oliver
和另一个包namedoliver.twist
之间,或者包namedevelyn.wood
和包之间没有特殊的访问关系evelyn.waugh
。也就是说,与任何其他包中的代码相比,命名包中的代码oliver.twist
无法更好地访问包中声明的类型oliver
。
有了这个背景,我们就可以回答这个问题了。由于包与其子包之间或父包的两个不同子包之间没有特殊的访问关系,因此语言中无法以请求的方式使方法对两个不同的包可见,同时限制其访问其他包。这是一个记录在案的、有意的设计决策。
方法可以公开并且所有包(包括odp.proj
和odp.proj.test
)都可以访问给定的方法,或者可以将方法设为私有(默认可见性),并且所有需要直接访问它的代码都必须放入与方法相同的(子)包。
关于测试用例,Java 中的标准做法是将类型的测试代码与其源代码放在同一个包中,但放在文件系统上的不同位置。例如,在Maven和Gradle构建工具中,约定是将源src/main/java/odp/proj
文件放在
src/test/java/odp/proj
. 当构建工具编译时,两个目录中的项目最终都在odp.proj
包中,但只有src
文件包含在生产工件中;测试文件仅在构建时用于验证生产文件。通过这种设置,测试代码可以自由访问它正在测试的代码的任何包私有或受保护代码,因为它们将在同一个包中。
如果您希望跨子包或非测试/生产案例的同级包共享代码,我见过一些库使用的一种解决方案是将该共享代码设为公开,但记录它用于内部库仅使用。
这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
并为它不存在的测试方法创建适当的文件夹。
当我在 IntelliJ 中执行此操作时,我的源代码树如下所示:
src // source root
- odp
- proj // .java source here
- test // test root
- odp
- proj // JUnit or TestNG source here
正如其他人所解释的那样,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 中没有子包的概念,有没有办法解决这个问题?我有一些方法,我希望只对测试人员和该包的其他成员可用。
这可能取决于您不显示它们的动机,但如果唯一的原因是您不想用仅用于测试(或其他一些内部事物)的东西污染公共界面,我会将这些方法放在一个单独的公共接口并让“隐藏”方法的使用者使用该接口。它不会阻止其他人使用该界面,但我认为您没有理由应该这样做。
对于单元测试,如果可以不重写批次,请按照建议使用相同的包。
如果没有将访问修饰符放在方法前面,您就说它是包私有的。
看下面的例子。
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);
}
}
你不能。在 Java 中没有子包的概念,因此
odp.proj
和odp.proj.test
是完全独立的包。