【问题标题】:The opens directive in Java 9Java 9 中的 opens 指令
【发布时间】:2017-05-17 12:32:46
【问题描述】:

我正在阅读 Java 9 规范的草案,但我不清楚这句话:

opens 指令指定当前模块要打开的包的名称。这使得包中的公共和受保护类型,以及它们的公共和受保护成员,只能在运行时被其他模块中的代码访问。它还使包中的所有类型及其所有成员都可以通过 Java SE 平台的反射库进行访问。

如果 opens 使 public 和 protected 仅在运行时可访问,那么包区域中的所有类型都可以通过反射访问意味着什么?

我不明白运行时和反射之间的区别。

似乎打开的包只能在运行时访问并受保护(通过反射?),以及其他未指定类型和成员的包可访问 vie 反射(也是私有的......)。

【问题讨论】:

  • 我不知道我是否正确理解了您的问题,但请考虑到有人可能拥有依赖于包中公共 API 的代码。然后 Java 9 出来了,包被封装在一个模块中。您需要打开模块才能继续使用该遗留代码。
  • 我再次尝试解释一下:如果 open 允许一个包的所有类型和所有成员进行反射访问,为什么还要解释一个 public 和 protected case?
  • 好的,谢谢。就像我说的那样,可能有人在封装包之前使用了这些 API(正常方式,而不是通过反射)。这似乎是一个向后兼容的功能。
  • 请问如何在运行时访问 API(公共和受保护)而不进行反射? (在编译时无法访问打开的包......)。也许公共和受保护将通过正常反射访问,而私有通过深度反射访问?

标签: java java-9


【解决方案1】:

假设您编写了一些使用库中的公共类的代码。

import somelibrary.somepackage.SomeClass; // <-- public class from a library.

public class Main {

    public static void main(String[] args) {
        SomeClass.doSomething();
    }

}

然后你编译这段代码,它编译得很好,因为你使用的类是public

现在,在库的下一个版本中,包被添加到模块中,但不导出。这意味着,如果您尝试在运行时模块路径上使用此新模块运行代码,则会引发异常,因为您正在尝试访问封装的包。

为了让你的代码再次工作,你可以使用命令行选项打开这个模块给你的代码,这样它就可以继续使用封装好的包。

或者,库的创建者可以将opens somepackage; 添加到库的模块定义中。这将允许您使用这个新版本运行您的代码,但不能使用它编译。 IE。 publicprotected 成员只能在运行时访问,但不涉及反射。


当您扩展一个类并想要访问封装包中超类的protected 成员时也是如此。

但是 opens 指令并没有改变这样一个事实,即如果在库的下一个版本中,一个方法或字段被创建为private,如果你尝试使用它,你会得到一个IllegalAccessError

class SomeClass { // <-- the class in the library
    public static void doSomething() {
        System.out.println("doSomething"); // contrived example code
    }
}

...

public class Main {    
    public static void main(String... args) throws Exception {      
        SomeClass.doSomething(); // this call compiles fine,
    }    
}

然后,在库的下一个版本中,doSomething 变成了private

private static void doSomething() {...}

并重新编译。但是,如果您尝试使用新版本的SomeClass 运行旧版本的Main,则会得到IllegalAccessError

简而言之,opens 仅适用于新版本库中仍为 publicprotected 的成员。


但是,在反射的情况下,您始终可以使用setAccessible(true) 访问private 成员:

public static void main(String... args) throws Exception {      
    Method m = SomeClass.class.getDeclaredMethod("doSomething");
    m.setAccessible(true);
    m.invoke(null); // works Fine
}

所以在反射的情况下,opens 也会使封装的私有成员再次可访问。

【讨论】:

  • 是的,这是一个很好的答案,所以我投了赞成票,但在这种情况下,我也可以访问私人成员......所以我还是不明白规范中表达的差异。真诚地清除了@xenteros 发布的文档中所说的内容,但同时我认为规范没有错误......即使它还在草稿中!
  • @xdevel2000 不,在这种情况下,您无法访问 private 成员(您针对 public API 进行编译,在更高版本中使用 private)。你会得到一个IllegalAccessError,并且使用 opens 不会改变这一点。
  • 拜托,你能给我提供一个 IllegalAccessError 的完整示例吗?现在我不能自己尝试这个......如果可以的话,当然:)
  • @xdevel2000 我添加了一些示例,希望对您有所帮助:)
  • @xenteros 我在这里没有明确使用它,但是当包含SomeClass 包的模块不再导出该包时,它可以在运行Main 时用作命令行选项。即--add-opens com.mylib/com.mylib=main,其中main是依赖模块,com.mylib是依赖模块,com.mylib是带有SomeClass的包。或者,或者(这并不适合我的示例),它可以像 opens com.mylib to main; 中的 module-info.javacom.mylib 一样使用。
【解决方案2】:

模块打开的包,可以是合格的,也可以是不合格的。

模块声明中的 opens 指令将包声明为 打开以允许包中的所有类型及其所有成员,而不是 只有公共类型及其公共成员由 API 反映 支持私人访问或绕过或抑制默认值的方法 Java 语言访问控制检查。

--Documentation

【讨论】:

  • 好...所以它似乎与规范相矛盾...也许是因为它在草稿中?
猜你喜欢
  • 1970-01-01
  • 2017-10-18
  • 1970-01-01
  • 2021-07-10
  • 2020-05-30
  • 2018-09-30
  • 1970-01-01
  • 2011-01-25
  • 2020-07-15
相关资源
最近更新 更多