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:
CharacterandStory. - We want to expose
Character‘s non-public methods toStorybut 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.Storyis located in a different package.- Normally
Storywouldn’t be able to invokeCharacter.getPhrase(); however,SharedSecretsallowsCharacterto share access with classes that it trusts. StoryinvokesSharedSecrets.INSTANCE.secretCharacterwhich uses an anonymous nested class to accessCharacter‘s internals.external.story.Storycan accessinternal.secrets.SharedSecretsbecause the two are located in the same module, but external users cannot access it becausemodule-info.javadoes not export that package.
If you want to export SharedSecrets to trusted external modules, see https://stackoverflow.com/a/53653651/14731.