jdk.internal.misc.SharedSecrets
describes itself as:
A repository of “shared secrets”, which are a mechanism for calling implementation-private methods in another package without using reflection. A package-private class implements a public interface and provides the ability to call package-private methods within that package; the object implementing that interface is provided through a third package to which access is restricted. This framework avoids the primary disadvantage of using reflection for this purpose, namely the loss of compile-time checking.
Can someone please provide an example that demonstrates how this mechanism enables classes in one package to access package-private methods in a different package?
Advertisement
Answer
Quoting http://blog.fuseyism.com/index.php/2008/05/26/sharing-secrets/:
When looking through OpenJDK for the VM project, I noticed that they have a rather interesting solution to this. This is encapsulated in sun.misc.SharedSecrets. This class provides access to instances of a number of public interfaces, such as sun.misc.JavaLangAccess. The actual implementations are provided as inner classes in the appropriate package e.g. java.lang, where it has access to the private and package-private variables and methods within.
Here is a concrete example:
- We have two classes:
Character
andStory
. - We want to expose
Character
‘s non-public methods toStory
but not external users (classes residing outside the module).
Main.java:
package external.main; import external.character.Character; import external.story.Story; public class Main { 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); } }
Story.java
package external.story; import external.character.Character; import internal.secrets.SharedSecrets; public final class Story { /** * Introduces a character. * * @param character the character */ public void introduce(Character character) { System.out.println(character.name() + " enters the room and says: " + SharedSecrets.INSTANCE.secretCharacter.getPhrase(character)); } }
Character.java:
package external.character; import internal.secrets.SecretCharacter; import internal.secrets.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(); } }; } /** * @return the character's introductory phrase */ abstract String getPhrase(); }
SharedSecrets.java:
package internal.secrets; public final class SharedSecrets { public static SharedSecrets INSTANCE = new SharedSecrets(); public SecretCharacter secretCharacter; /** * Prevent construction. */ private SharedSecrets() { } }
SecretCharacter.java:
package internal.secrets; import external.character.Character; public interface SecretCharacter { /** * @param character a character * @return the character's introductory phrase */ String getPhrase(Character character); }
module-info.java:
module SharedSecret { exports external.character; exports external.main; exports external.story; }
Output
HARRY_POTTER enters the room and says: Your bird, there was nothing I could do. He just caught fire.
RON_WEASLEY enters the room and says: Who are you and what have you done with Hermione Granger?
HERMIONE_GRANGER enters the room and says: I’m not an owl!
Explanation
external.character.Character.getPhrase()
is package-protected.external.story.Story
is located in a different package.- Normally
Story
wouldn’t be able to invokeCharacter.getPhrase()
; however,SharedSecrets
allowsCharacter
to share access with classes that it trusts. Story
invokesSharedSecrets.INSTANCE.secretCharacter
which uses an anonymous nested class to accessCharacter
‘s internals.external.story.Story
can accessinternal.secrets.SharedSecrets
because the two are located in the same module, but external users cannot access it becausemodule-info.java
does not export that package.
If you want to export SharedSecrets to trusted external modules, see https://stackoverflow.com/a/53653651/14731.