2

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?

Holger
  • 267,107
  • 35
  • 402
  • 715
Gili
  • 81,444
  • 90
  • 364
  • 657
  • 1
    Since this technique works since Java 1.1 and Sun’s `SharedSecrets` is not much younger, there is no relevance to `[java-9]`. This is illustrated by the fact that the article you’ve cited in the answer is almost ten years old. – Holger Oct 13 '17 at 07:08
  • ....and the only relevant change in JDK9 about `SharedSecrets` I could find [was this](https://stackoverflow.com/questions/46612648/javalangaccess-and-sharedsecrets-in-java-9) – Naman Oct 13 '17 at 07:45

1 Answers1

7

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:

External Users
├── external
│   └── EndUser.java
└── module-info.java


Library
├── library
│   ├── character
│   │   └── Character.java
│   ├── story
│   │   └── Story.java
│   └── secrets
│       ├── SharedSecrets.java
│       └── SecretCharacter.java
└── module-info.java
  • We want to expose Character's internals to Story without EndUser gaining access.

external/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);
    }
}

module-info.java:

module external
{
    requires library;
}

library/story/Story.java

package library.story;

import library.character.Character;
import library.secrets.SecretCharacter;
import library.secrets.SharedSecrets;

public final class Story
{
    private static final SecretCharacter secretCharacter =
      SharedSecrets.INSTANCE.secretCharacter;

    public void introduce(Character character)
    {
        System.out.println(character.name() + " enters the room and says: " + 
          secretCharacter.getPhrase(character));
    }
}

character/Character.java:

package library.character;

import library.secrets.SecretCharacter;
import library.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();
            }
        };
    }

    abstract String getPhrase();
}

library/secrets/SharedSecrets.java:

package library.secrets;

public enum SharedSecrets
{
    INSTANCE;
    public SecretCharacter secretCharacter;
}

library/secrets/SecretCharacter.java:

package library.secrets;

import library.character.Character;

public interface SecretCharacter
{
    String getPhrase(Character character);
}

module-info.java:

module library
{
    exports library.character;
    exports library.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

  • Character.getPhrase() is package-protected.
  • Story is located in a different package.
  • Normally Story wouldn't be able to invoke Character.getPhrase(); however, SharedSecrets allows Character to share access with classes that it trusts.
  • Story invokes SharedSecrets.INSTANCE.secretCharacter which uses an anonymous nested class to access Character's internals.
  • Story can access SharedSecrets because the two are located in the same module, but external users cannot access it because module-info.java does not export that package.

If you want to export SharedSecrets to trusted external modules, see https://stackoverflow.com/a/53653651/14731.

Gili
  • 81,444
  • 90
  • 364
  • 657