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
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:
Class
object.Class.getDeclaredField(...)
method to get a Field
object for the field.Field.setAccessible(true)
to turn off the access check.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:
(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.