Pregunta

I have following classses

Hello.java

package speak.hello;

import java.util.Map;

import speak.hi.CustomMap;
import speak.hi.Hi;

public class Hello {

    private Hi hi;

    Hello(Hi hi) {
        this.hi = hi;
    }

    public String sayHello() {
        return "Hello";
    }

    public String sayHi() {
        return hi.sayHi();
    }

    public Map<String, Object> getMap() {
        return hi.getMap();
    }

    public void clearMap() {
        hi.getMap().clear();
    }

    public void discardMap() {
        CustomMap map = (CustomMap) hi.getMap();
        map.discard();
    }

    public static void main(String[] args) {
        Hello hello = new Hello(new Hi());
        System.out.println(hello.sayHello());
        System.out.println(hello.sayHi());
        System.out.println(hello.getMap());
        hello.clearMap();
        System.out.println("--");
        hello.discardMap();
    }

}

Hi.java

package speak.hi;

import java.util.HashMap;
import java.util.Map;

public class Hi {
    public String sayHi() {
        return "Hi";
    }

    public Map<String, Object> getMap() {
        return new CustomMap<String, Object>();
    }
}

CustomMap.java

package speak.hi;

import java.util.HashMap;

public class CustomMap<K, V> extends HashMap<K, V> {
    private static final long serialVersionUID = -7979398843650044928L;

    public void discard() {
        System.out.println("Discarding Map");
        this.clearCache();
        this.clear();
    }

    @Override
    public void clear() {
        System.out.println("Clearing Map");
        super.clear();
    }

    private void clearCache() {
        System.out.println("Clearing Map");
    }
}

This works fine until I remove public access specifier from CustomMap

package speak.hi;

import java.util.HashMap;

class CustomMap<K, V> extends HashMap<K, V> {
    private static final long serialVersionUID = -7979398843650044928L;

    public void discard() {
        System.out.println("Discarding Map");
        this.clearCache();
        this.clear();
    }

    @Override
    public void clear() {
        System.out.println("Clearing Map");
        super.clear();
    }

    private void clearCache() {
        System.out.println("Clearing Map");
    }
}

Compiler yells that

The type speak.hi.CustomMap is not visible

Now If I don't have options to modify speak.hi.CustomMap (third party jar etc..) Is there any way I could still use CustomMap from speak.hello.Hello?


One option that I know is to move speak.hello.Hello to speak.hi.Hello as Now Hello is in package speak.hi it can access package private Class Hi


Is there any other way to do this ? Using reflection perhaps ?


EDIT :Updated with additional details as requested by @StephenC

¿Fue útil?

Solución 2

Following method Invokes default scoped class method using reflection

public void discardMap() {
    //CustomMap map = (CustomMap) hi.getMap();
    //map.discard();
    try {
        Object o =hi.getClass().getMethod("getMap").invoke(hi);
        Method m = o.getClass().getMethod("discard");
        m.setAccessible(true);
        m.invoke(o);
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

Otros consejos

Is there any other way to do this ? Using reflection perhaps ?

Yes. Reflection can be used to bypass the Java access rules, if your application has full privilege.

For instance, to access a private field of an object from a different class, you need to:

  • Get the object's Class object.
  • Use the Class.getDeclaredField(...) method to get a Field object for the field.
  • Call Field.setAccessible(true) to turn off the access check.
  • Call Class.getField(object, Field) to get the field's value (or boxed value if it is a primitive type).

If the class itself is not accessible, you need to make sure that you don't refer to the classes identifier in your source code ... 'cos that will result in a compilation error. Instead, assign its reference to (say) variable of type Object or of some other visible supertype, and perform more specific operations on the instance reflectively.


As you might imagine, this is tedious and error prone. You'd be advised to find a better way, like:

  • getting the suppliers of the classes to fix whatever is causing you to need to break the visibility restrictions,
  • getting the suppliers of the classes to change their visibility,
  • finding another way to use the classes that doesn't require breaking open their abstraction, or
  • ditching them and finding (or writing) something better.

(Generally speaking, if you have to break open an abstraction then something is wrong with either the abstraction itself or the way you are using it.)


Finally, I should add that untrusted code is (should be) run in a security sandbox that blocks the use of the key reflective operations.

It may not be possible because:

Class :

Accessible to class from same package?

  • public : yes
  • protected : yes
  • default : yes
  • private : no

Accessible to class from different package?

  • public : yes
  • protected : no
  • default : unless it is a subclass
  • private : no

I don't recommend to use non-API classes as they might change in any future version and can break your code.

How did you find out about this class? Is it an Open Source library?

Try to contact the authors of the library, tell them your use case and find a way with them to offer a public API. If it's an open source library you could help them by providing a patch.

Adding this solution for sake of completeness.

One option that I know is to move speak.hello.Hello to speak.hi.Hello as Now Hello is in package speak.hi it can access package private Class Hi

package speak.hi;

public class Hello {

    private Hi hi;

    Hello(Hi hi) {
        this.hi = hi;
    }

    public String sayHello() {
        return "Hello";
    }

    public String sayHi() {
        return hi.sayHi();
    }

    public static void main(String[] args) {
        Hello hello = new Hello(new Hi());
        System.out.println(hello.sayHello());
        System.out.println(hello.sayHi());
    }

}

Not possible. The security model is this : a model to provide security :) If you designed class Hi and delivered it to customers with private access, you wouldn't like them to be able to bypass your restrictions, would you?

I would think that if the authors of a library did not make a particular class part of the public API, it is because they don't want other people using it. You should respect the decision even though you can break it using reflection. Using private API is simply bad programming.

1st class is in package a.b.c.class1 but class1 is private as well as abstract 2nd class is in package a.b.c.class2 extends class1 but class2 is public

3rd class is in package x.y.z.class3

So as to access class1 in class 3 you can write something like:-

Class baseClass = (new class2()).getClass(); and use the instance of its superclass then use:- baseClass.getSuperClass(); and use it wherever you want.

But then again the baseclass was made abstract and private for a reason hence not advisable to do so but then again this solution could be used as a workaround.

Not Possible you can not create your Hi class as private.

I think by default the class will be "default" (package private, you can say), NOT "private". So it can be accessed with in the same package.

Moreover, you CANNOT make any *top level class Private in Java.

And if you want make a class default and still be able to access it in other package then what will be the purpose of having access specifiers (modifiers) ??

you either need to make class public or move to the same package.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top