I have some class storing keys with important information. No one else is allowed to create a key, since a key relys on static information (like certain directory structures etc.).
public final class KeyConstants
{
private KeyConstants()
{
// could throw an exception to prevent instantiation
}
public static final Key<MyClass> MY_CLASS_DATA = new Key<MyClass>("someId", MyClass.class);
public static class Key<T>
{
public final String ID;
public final Class<T> CLAZZ;
private Key(String id, Class<T> clazz)
{
this.ID = id;
this.CLAZZ = clazz;
}
}
}
This example is simplyfied.
I wanted to test the consequences of a wrong key (exception handling, etc.) and instantiated the class via reflection in a JUnit test case.
Constructor<?> c = KeyConstants.Key.class.getDeclaredConstructor(String.class, Class.class);
c.setAccessible(true);
@SuppressWarnings ("unchecked")
KeyConstants.Key<MyClass> r = (KeyConstants.Key<MyClass>) c.newInstance("wrongId", MyClass.class);
Then I asked myself how could I prevent further instantiation of the key class (i. e. preventing further object creating via reflection)?
enums
came to my mind, but they don't work with generics.
public enum Key<T>
{
//... Syntax error, enum declaration cannot have type parameters
}
So how can I keep a set of n
instances of a generic class and prevent further instantiation?
So how can I keep a set of n instances of a generic class and prevent further instantiation?
If you truly want to use this pattern, then no one (including you) should be able to instantiate a Key
object. In order to keep a set of n instances in a class with this pattern, you could have a private constructor, a static method for access and a SecurityManager to prevent reflection. And since you want to be able to access the keys as pubic constants, I would try something like this..
public class KeyConstants{
// Here are your n instances for public access
public static final int KEY_1 = 1;
public static final int KEY_2 = 2;
.
.
.
public static final int KEY_N = 'n';
// now you can call this method like this..
// Key mKey = KeyConstants.getKey(KeyConstants.KEY_1);
public static Key getKey(int key){
List keys = Key.getInstances();
switch(key){
case KEY_1:
return keys.get(0);
case KEY_2:
return keys.get(1);
.
.
.
case KEY_N:
return keys.get(n);
default:
// not index out of bounds.. this means
// they didn't use a constant
throw new IllegalArgumentException();
}
}
static class Key<T>{
private static List<Key> instances;
private String ID;
private Class<T> CLAZZ;
private Key(String id, Class<T> clazz){
this.ID = id;
this.CLAZZ = clazz;
}
public static List<Key> getInstances(){
if(instances == null){
instances = new ArrayList<Key>();
//populate instances list
}
return instances;
}
}
}
Use SecurityManager
to prevent reflection access.
//attempt to set your own security manager to prevent reflection
try {
System.setSecurityManager(new MySecurityManager());
} catch (SecurityException se) {
}
class MySecurityManager extends SecurityManager {
public void checkPermission(Permission perm) {
if (perm.getName().equals("suppressAccessChecks"))
throw new SecurityException("Invalid Access");
}
}
This will throw a SecurityException
anytime someone attempts to access a private variable or field in your class (including access attempts via reflection).
I'm not sure I fully understand your question, but if a private constructor is not sufficient, can you use a more dynamic approach and throw an exception in the constructor after a signal is given? For example:
public static class Key<T>
{
private static boolean isLocked = false;
// Call this method when you want no more keys to be created
public static void lock() { isLocked = true; }
...
private Key(String id, Class<T> clazz)
{
if (isLocked) throw new IllegalStateException("Cannot create instances of Key");
this.ID = id;
this.CLAZZ = clazz;
}
}
Then - and this is the disadvantage - you will have to call Key.lock()
once you want to prevent more instances being created.
As you showed in your code to prevent instantiating KeyConstants
you can throw some Exception inside private-non-argument constructor.
Harder part is way to block creating KeyConstants.Key constructor from outside of KeyConstants
class.
Maybe create Exception in your constructor and check how its stack trace looks like. When I add this code to constructor
private Key(String id, Class<T> clazz) {
StackTraceElement[] stack = new Exception().getStackTrace();
for (int i=0; i<stack.length; i++){
System.out.println(i+") "+stack[i]);
}
this.ID = id;
this.CLAZZ = clazz;
}
and create instance of Key with reflection like
Constructor<?> c = KeyConstants.Key.class.getDeclaredConstructor(
String.class, Class.class);
c.setAccessible(true);
KeyConstants.Key<MyClass> r = (KeyConstants.Key<MyClass>) c
.newInstance("wrongId", MyClass.class);
I get
0) KeyConstants$Key.<init>(Test.java:38)
1) sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
2) sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
3) sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
4) java.lang.reflect.Constructor.newInstance(Constructor.java:525)
so maybe just if 4th element of stack is java.lang.reflect.Constructor.newInstance
throw Exception to prevent executing rest of constructors code like:
if (stack.length>=4 && stack[4].toString().startsWith("java.lang.reflect.Constructor.newInstance")){
throw new RuntimeException("cant create object with reflection");
}
I have another approach, you can bound an interface in a way to only be implemented by enum
.
With that approach you have a fixed set of instances at compile time.
If you want to add lazy loading, the enums implementing it should be proxies that load the desired object if it is requested. The class or classes that are hidden behind the proxies should only be visible to them, so that they have exclusive access to the constructor.
public class User {
public static <S> S handleKey(FixedInstanceSet<S,?> key) {
return key.getKey();
}
}
interface FixedInstanceSet<S, T extends Enum<T> & FixedInstanceSet<S,T>>
{
public S getKey();
}
enum StringKeys implements FixedInstanceSet<String, StringKeys> {
TOP, DOWN, LEFT, RIGHT;
@Override
public String getKey() { return null; }
}
enum IntKeys implements FixedInstanceSet<Integer, IntKeys > {
TOP, DOWN, LEFT, RIGHT;
@Override
public Integer getKey() { return null; }
}
/*
* Bound mismatch: The type NotWorking is not a valid substitute for the bounded
* parameter <T extends Enum<T> & FixedInstanceSet<S,T>> of the type
* FixedInstanceSet<S,T>
*/
//class NotCompiling implements FixedInstanceSet<String, NotCompiling> {
//
// @Override
// public String getKey() { return null; }
//}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With