Java:子包可见性?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1967229/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 01:53:50  来源:igfitidea点击:

Java: Subpackage visibility?

javapackagevisibilityencapsulation

提问by Nick Heiner

I have two packages in my project: odp.projand odp.proj.test. There are certain methods that I want to be visible only to the classes in these two packages. How can I do this?

我的项目中有两个包:odp.projodp.proj.test. 有些方法我只想对这两个包中的类可见。我怎样才能做到这一点?

EDIT:If there is no concept of a subpackage in Java, is there any way around this? I have certain methods that I want to be available only to testers and other members of that package. Should I just throw everything into the same package? Use extensive reflection?

编辑:如果 Java 中没有子包的概念,有没有办法解决这个问题?我有一些方法,我希望只对测试人员和该包的其他成员可用。我应该把所有东西都扔进同一个包里吗?使用广泛的反射?

回答by starblue

You can't. In Java there is no concept of a subpackage, so odp.projand odp.proj.testare completely separate packages.

你不能。在 Java 中没有子包的概念,因此odp.projodp.proj.test是完全独立的包。

回答by peter.murray.rust

This is no special relation between odp.projand odp.proj.test- they just happen to be named as apparently related.

odp.proj和和之间没有特殊关系odp.proj.test——它们只是碰巧被命名为明显相关。

If the odp.proj.test package is simply providing tests then you can use the same package name (odp.proj). IDEs like Eclipse and Netbeans will create separate folders (src/main/java/odp/projand src/test/java/odp/proj) with the same package name but with JUnit semantics.

如果 odp.proj.test 包只是提供测试,那么您可以使用相同的包名 ( odp.proj)。Eclipse 和 Netbeans 等 IDE 将创建具有相同包名但具有 JUnit 语义的单独文件夹(src/main/java/odp/projsrc/test/java/odp/proj)。

Note that these IDEs will generate tests for methods in odp.projand create the appropriate folder for the test methods it doesn't exist.

请注意,这些 IDE 将为其中的方法生成测试,odp.proj并为它不存在的测试方法创建适当的文件夹。

回答by Asaph

The names of your packages hint that the application here is for unit testing. The typical pattern used is to put the classes you wish to test and the unit test code in the same package (in your case odp.proj) but in different source trees. So you would put your classes in src/odp/projand your test code in test/odp/proj.

包的名称暗示这里的应用程序用于单元测试。使用的典型模式是将您希望测试的类和单元测试代码放在同一个包中(在您的情况下odp.proj),但放在不同的源代码树中。所以你会把你的类src/odp/proj放在test/odp/proj.

Java does have the "package" access modifier which is the default access modifier when none is specified (ie. you don't specify public, private or protected). With the "package" access modifier, only classes in odp.projwill have access to the methods. But keep in mind that in Java, the access modifiers cannot be relied upon to enforce access rules because with reflection, any access is possible. Access modifiers are merely suggestive (unless a restrictive security manager is present).

Java 确实有“包”访问修饰符,当没有指定时,它是默认的访问修饰符(即您没有指定 public、private 或 protected)。使用“包”访问修饰符,只有其中的类odp.proj才能访问方法。但请记住,在 Java 中,不能依赖访问修饰符来强制执行访问规则,因为通过反射,任何访问都是可能的。访问修饰符只是提示性的(除非存在限制性安全管理器)。

回答by Alberto Zaccagni

Without putting the access modifier in front of the method you say that it is package private.
Look at the following example.

如果没有将访问修饰符放在方法前面,您就说它是包私有的。
看下面的例子。

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
    }
}

回答by Fredrik

EDIT: If there is no concept of a subpackage in Java, is there any way around this? I have certain methods that I want to be available only to testers and other members of that package.

编辑:如果 Java 中没有子包的概念,有没有办法解决这个问题?我有一些方法,我希望只对测试人员和该包的其他成员可用。

It probably depends a bit on your motives for not displaying them but if the only reason is that you don't want to pollute the public interface with the things intended only for testing (or some other internal thing) I would put the methods in a separate public interface and have the consumers of the "hidden" methods use that interface. It will not stop others from using the interface but I see no reason why you should.

这可能取决于您不显示它们的动机,但如果唯一的原因是您不想用仅用于测试(或其他一些内部事物)的东西污染公共界面,我会将这些方法放在一个单独的公共接口并让“隐藏”方法的使用者使用该接口。它不会阻止其他人使用该界面,但我认为您没有理由应该这样做。

For unit tests, and if it is possible without rewriting the lot, follow the suggestions to use the same package.

对于单元测试,如果可以不重写批次,请按照建议使用相同的包。

回答by duffymo

When I do this in IntelliJ, my source tree looks like this:

当我在 IntelliJ 中执行此操作时,我的源代码树如下所示:

src         // source root
- odp
   - proj   // .java source here
- test      // test root
  - odp
     - proj // JUnit or TestNG source here

回答by qxo

With the PackageVisibleHelper class, and keep it private before PackageVisibleHelperFactory frozen, we can invoke the launchA(by PackageVisibleHelper ) method in anywhere:)

使用 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); 
    }
}

回答by ndm13

As others have explained, there is no such thing as a "subpackage" in Java: all packages are isolated and inherit nothing from their parents.

正如其他人所解释的那样,Java 中没有“子包”这样的东西:所有包都是孤立的,并且不从它们的父代继承任何东西。

An easy way to access protected class members from another package is to extend the class and override the members.

从另一个包访问受保护的类成员的一种简单方法是扩展类并覆盖成员。

For instance, to access ClassInAin package a.b:

例如,要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(); }
}

make a class in that package that overrides the methods you need in ClassInA:

在该包中创建一个类来覆盖您需要的方法ClassInA

package a.b;

import a.ClassInA;

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

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

That lets you use the overriding class in place of the class in the other package:

这使您可以使用覆盖类代替另一个包中的类:

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

Note that this only works for protected members, which are visible to extending classes (inheritance), and not package-private members which are visible only to sub/extending classes within the same package. Hopefully this helps someone!

请注意,这仅适用于对扩展类(继承)可见的受保护成员,而不适用于仅对同一包中的子/扩展类可见的包私有成员。希望这对某人有所帮助!

回答by M. Justin

Most of the answers here have stated that there is no such thing as a subpackage in Java, but that is not strictly accurate. This term has been in the Java Language Specification as far back as Java 6, and probably further back (there does not seem to be a freely accessible version of the JLS for earlier versions of Java). The language around subpackages hasn't changed much in the JLS since Java 6.

这里的大多数答案都表明 Java 中没有子包这样的东西,但这并不完全准确。这个术语早在 Java 6 中就已经出现在 Java 语言规范中,可能更早一些(对于早期版本的 Java,似乎没有可免费访问的 JLS 版本)。自 Java 6 以来,围绕子包的语言在 JLS 中没有太大变化。

Java 13 JLS:

Java 13 JLS

The members of a package are its subpackages and all the top level class types and top level interface types declared in all the compilation units of the package.

For example, in the Java SE Platform API:

  • The package javahas subpackages awt, applet, io, lang, net, and util, but no compilation units.
  • The package java.awthas a subpackage named image, as well as a number of compilation units containing declarations of class and interface types.

包的成员是它的子包以及包的所有编译单元中声明的所有顶级类类型和顶级接口类型。

例如,在 Java SE Platform API 中:

  • 封装java有子包awtappletiolangnet,和util,但没有编译单元。
  • 该包java.awt有一个名为 的子包image,以及许多包含类和接口类型声明的编译单元。

The subpackage concept is relevant, as is enforces naming constraints between packages and classes/interfaces:

子包概念是相关的,因为它在包和类/接口之间强制命名约束:

A package may not contain two members of the same name, or a compile-time error results.

Here are some examples:

  • Because the package java.awthas a subpackage image, it cannot (and does not) contain a declaration of a class or interface type named image.
  • If there is a package named mouseand a member type Buttonin that package (which then might be referred to as mouse.Button), then there cannot be any package with the fully qualified name mouse.Buttonor mouse.Button.Click.
  • If com.nighthacks.java.jagis the fully qualified name of a type, then there cannot be any package whose fully qualified name is either com.nighthacks.java.jagor com.nighthacks.java.jag.scrabble.

一个包可能不包含两个同名成员,否则会导致编译时错误。

这里有些例子:

  • 因为包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

However, this naming restriction is the onlysignificance afforded to subpackages by the language:

但是,这种命名限制是语言赋予子包的唯一意义:

The hierarchical naming structure for packages is intended to be convenient for organizing related packages in a conventional manner, but has no significance in itself other than the prohibition against a package having a subpackage with the same simple name as a top level type declared in that package.

For example, there is no special access relationship between a package named oliverand another package named oliver.twist, or between packages named evelyn.woodand evelyn.waugh. That is, the code in a package named oliver.twisthas no better access to the types declared within package oliverthan code in any other package.

包的分层命名结构旨在方便以常规方式组织相关包,但除了禁止包具有与该包中声明的顶级类型具有相同简单名称的子包外,其本身没有任何意义.

例如,named 包oliver和另一个包namedoliver.twist之间,或包namedevelyn.wood和包之间没有特殊的访问关系evelyn.waugh。也就是说,与任何其他包中的代码相比,命名包中的代码oliver.twist无法更好地访问包中声明的类型oliver



With this context, we can answer the question itself. Since there is explicitly no special access relationship between a package and its subpackage, or between two different subpackages of a parent package, there is no way within the language to make a method visible to two different packages in the requested manner. This is a documented, intentional design decision.

有了这个背景,我们就可以回答这个问题了。由于包与其子包之间或父包的两个不同子包之间没有明确的特殊访问关系,因此语言中无法以请求的方式使方法对两个不同的包可见。这是一个记录在案的、有意的设计决策。

Either the method can be made public and all packages (including odp.projand odp.proj.test) will be able to access the given methods, or the method could be made package private (the default visibility), and all the code that needs to directly access it must put in the same (sub)package as the method.

方法可以公开并且所有包(包括odp.projodp.proj.test)都可以访问给定的方法,或者可以将方法设为包私有(默认可见性),并且所有需要直接访问它的代码都必须放入与方法相同的(子)包。

That said, a very standard practice in Java is to put the test code in the same package as the source code, but in a different location on the file system. For instance, in the Mavenbuild tool, the convention would be to put these source and test files in src/main/java/odp/projand src/test/java/odp/proj, respectively. When the build tool compiles this, both sets of files end up in the odp.projpackage, but only the srcfiles are included in the production artifact; the test files are only used at build time to verify the production files. With this setup, test code can freely access any package private or protected code of the code it's testing, as they will be in the same package.

也就是说,Java 中一个非常标准的做法是将测试代码与源代码放在同一个包中,但放在文件系统上的不同位置。例如,在Maven构建工具中,约定是将这些源文件和测试文件分别放在src/main/java/odp/proj和 中 src/test/java/odp/proj。当构建工具编译它时,这两组文件都在odp.proj包中,但只有src文件包含在生产工件中;测试文件仅在构建时用于验证生产文件。通过这种设置,测试代码可以自由访问它正在测试的代码的任何包私有或受保护代码,因为它们将在同一个包中。

In the case where you want code sharing across subpackages or sibling packages that isn't the test/production case, one solution I've seen some libraries use is to put that shared code as public, but document that it is intended for internal library use only.

如果您希望跨子包或非测试/生产案例的同级包共享代码,我见过一些库使用的一种解决方案是将该共享代码设为公开,但记录它是用于内部库仅使用。