Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Access package private Class from a Class in some other package?

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

like image 234
Prashant Bhate Avatar asked Nov 25 '11 10:11

Prashant Bhate


People also ask

Can we access private class outside the package?

Private: The class members declared as private can be accessed only by the functions inside the class. They are not allowed to be accessed directly by any object or function outside the class.

Can we access private class outside the package in Java?

You can access the private methods of a class using java reflection package. Step1 − Instantiate the Method class of the java. lang. reflect package by passing the method name of the method which is declared private.

How do you access private instance outside the class or subclass?

To access private variables of parent class in subclass you can use protected or add getters and setters to private variables in parent class.. Save this answer. Show activity on this post. You can't access directly any private variables of a class from outside directly.


2 Answers

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.

like image 59
Stephen C Avatar answered Nov 06 '22 16:11

Stephen C


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();
    }
}
like image 39
Prashant Bhate Avatar answered Nov 06 '22 18:11

Prashant Bhate