Quoting 安德鲁·约翰·休斯:
当浏览 OpenJDK 的 VM 项目时,我注意到他们对此有一个相当有趣的解决方案。这被封装在 sun.misc.SharedSecrets 中。此类提供对许多公共接口实例的访问,例如 sun.misc.JavaLangAccess。实际的实现作为适当包中的内部类提供,例如java.lang,它可以访问其中的私有和包私有变量和方法。
假设您的 API 类分散在多个包中。您希望他们能够访问彼此的内部结构,而不会将其暴露给最终用户。你做什么工作?
选项 1:不使用 Java 模块
- 创建一个将从公共 Javadoc 中省略的“内部”包(例如com.example.internal)
- 在内部包中声明一个或多个接口,引用您尝试访问的私有功能。
- 声明一个公共类(例如共享秘密)在内部包中保存这些接口的实现。
- 在 API 类中使用静态初始化器来获取/设置这些接口的实现共享秘密.
- 现在,API 类可以通过受信任的中介来访问彼此的内部结构(共享秘密).
选项 2:使用 Java 模块
- 假设您有两个模块:main and test,而你想要test访问内部的私有或包私有方法和字段main.
- 声明一个公共类共享秘密在主模块内部,在非导出的包中。例如:main.internal.SharedSecrets.
- In main's 模块信息.java, add
exports main.internal to test
.
- 意思是,包主.内部仅可由模块访问test.
- Because 共享秘密是公开的,任何人main(甚至来自不同的包)可以将桥接功能或字段推入其中。实际上它也可以以另一种方式工作(test可以将桥接功能推入main)但迄今为止我从未需要这样做。
- 现在,任何时候test希望访问内部结构main,它只是通过调用共享秘密.
这个解决方案特别好,因为生成的 Javadoc 和 IDE 自动完成功能看起来会干净很多。
具体例子
External Users
├── external
│ └── EndUser.java
└── module-info.java
Library
├── library
│ ├── character
│ │ └── Character.java
│ ├── story
│ │ └── Story.java
│ └── internal
│ ├── SharedSecrets.java
│ └── SecretCharacter.java
└── module-info.java
- 我们想要曝光
Character
的内部结构为Story
没有EndUser
获得访问权限。
最终用户代码
外部/EndUser.java:
package external;
import library.character.Character;
import library.story.Story;
public class EndUser
{
public static void main(String[] args)
{
Story story = new Story();
story.introduce(Character.HARRY_POTTER);
story.introduce(Character.RON_WEASLEY);
story.introduce(Character.HERMIONE_GRANGER);
}
}
模块信息.java:
module external
{
requires library;
}
库代码
库/故事/Story.java
package library.story;
import library.character.Character;
import library.internal.SecretCharacter;
import library.internal.SharedSecrets;
public final class Story
{
private static final SharedSecrets sharedSecrets =
SharedSecrets.INSTANCE;
public void introduce(Character character)
{
System.out.println(character.name() + " enters the room and says: " +
sharedSecrets.secretCharacter.getPhrase(character));
}
}
库/字符/Character.java:
package library.character;
import library.internal.SecretCharacter;
import library.internal.SharedSecrets;
public enum Character
{
HARRY_POTTER
{
@Override
String getPhrase()
{
return "Your bird, there was nothing I could do. He just caught fire.";
}
},
RON_WEASLEY
{
@Override
String getPhrase()
{
return "Who are you and what have you done with Hermione Granger?";
}
},
HERMIONE_GRANGER
{
@Override
String getPhrase()
{
return "I'm not an owl!";
}
};
static
{
SharedSecrets.INSTANCE.secretCharacter = new SecretCharacter()
{
@Override
public String getPhrase(Character character)
{
return character.getPhrase();
}
};
}
abstract String getPhrase();
}
库/内部/SharedSecrets.java:
package library.internal;
public enum SharedSecrets
{
INSTANCE;
public SecretCharacter secretCharacter;
}
库/内部/SecretCharacter.java:
package library.internal;
import library.character.Character;
public interface SecretCharacter
{
String getPhrase(Character character);
}
模块信息.java:
module library
{
exports library.character;
exports library.story;
}
Output
哈利波特走进房间说:你的鸟儿,我无能为力。他刚刚着火了。
罗恩·韦斯利走进房间并说道:你是谁?你对赫敏·格兰杰做了什么?
HERMIONE_GRANGER 走进房间并说道: 我不是猫头鹰!
Notice
-
Character.getPhrase()
受包保护。
-
Story
位于不同的包中。
- 通常情况下
Story
将无法调用Character.getPhrase()
;然而,SharedSecrets
allows Character
与其信任的类共享访问权限。
-
Story
调用SharedSecrets.INSTANCE.secretCharacter
它使用匿名嵌套类来访问Character
的内部结构。
-
Story
可以访问SharedSecrets
因为两者位于同一个模块中,但是外部用户无法访问它,因为module-info.java
不导出该包。