Best practice for preventing further instantiation of Java classes

I have a class storage key with important information. No one else is allowed to create the key, since the key relies on static information (for example, some 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 simplified.

I wanted to check the consequences of the wrong key (exception handling, etc.) and create an instance of the class through reflection in the 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 can I prevent the further creation of an instance of the key class (i.e. prevent the further creation of the object through reflection)?

enums come to mind, but they do not work with generics.

 public enum Key<T> { //... Syntax error, enum declaration cannot have type parameters } 

So, how can I save a set of instances of n universal class and prevent subsequent instance creation?

+6
source share
6 answers

So, how can I save a set of n instances of a common class and prevent a further instance?

If you really want to use this template, no one (including you) can instantiate the Key object. To save a set of n instances in a class with this template, you can have a private constructor, a static access method, and a SecurityManager to prevent reflection. And since you want to have access to 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 access to reflection.

 //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 raise a SecurityException at any time when someone tries to access a private variable or field of your class (including access attempts via reflection).

+3
source

I'm not sure I fully understand your question, but if a private constructor is not enough, can you use a more dynamic approach and throw an exception in the constructor after the signal is given? For instance:

 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 a flaw - you will need to call Key.lock() to prevent creating more instances.

+1
source

As you have shown in your code, to prevent the creation of an instance of KeyConstants , you can throw some exception inside the private-non-argument constructor.

The harder part is the way to block the creation of the KeyConstants.Key constructor from outside the KeyConstants class.

Some wild idea

Perhaps throw an exception in your constructor and check what its stack trace looks like. When I add this code to the 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 an instance of the key with reflection, for example

 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 that it’s only possible if the 4th element of the stack is java.lang.reflect.Constructor.newInstance throw Exception, to prevent the rest of the constructors code from executing, for example:

 if (stack.length>=4 && stack[4].toString().startsWith("java.lang.reflect.Constructor.newInstance")){ throw new RuntimeException("cant create object with reflection"); } 
+1
source

I recently met several Multiton patterns where I tried to handle problems with unique enum keys, which gave me an idea of ​​a different approach.

Keys can be used to stream information, as I suggested, or even as keys for a heterogeneous type container, where they can compile.

Keyboard shortcuts

 public class KeyConstants { public static final KeysForIntegers SOME_INT_KEY = KeysForIntegers.KEY_2; public static final KeysForStrings SOME_STRING_KEY = KeysForStrings.KEY_1; public interface Key<Type> { public Class<Type> getType(); } /* Define methods that classes working with the keys expect from them */ public interface KeyInformation { public String getInfo1(); // and so on... } public enum KeysForStrings implements Key<String>, KeyInformation { KEY_1("someId"); public final String ID; private KeysForStrings(String id) { ID = id; } @Override public String getInfo1() { return "Good piece of information on " + ID + "."; } @Override public Class<String> getType() { return String.class; } } public enum KeysForIntegers implements Key<Integer>, KeyInformation { KEY_2("bla"); public final String ID; private KeysForIntegers(String id) { this.ID = id; } @Override public String getInfo1() { return "Some info on " + ID + "."; } @Override public Class<Integer> getType() { return Integer.class; } } } 

Key usage example

 public class KeyUser { public static void main(String[] args) { KeysForIntegers k1 = KeyConstants.SOME_INT_KEY; KeysForStrings k2 = KeyConstants.SOME_STRING_KEY; processStringKey(k2); useIntKey(k1); Integer i = useIntKey(KeyConstants.SOME_INT_KEY); processStringKey(KeyConstants.SOME_STRING_KEY); } /* My methods should just work with my keys */ @SuppressWarnings ("unchecked") public static <TYPE, KEY extends Enum<KeysForIntegers> & Key<TYPE> & KeyInformation> TYPE useIntKey(KEY k) { System.out.println(k.getInfo1()); return (TYPE) new Object(); } public static <KEY extends Enum<KeysForStrings> & KeyInformation> void processStringKey(KEY k) { System.out.println(k.getInfo1()); // process stuff } } 
+1
source

I have a different approach, you can bind the interface so that it can only be implemented by enum . With this approach, you have a fixed set of instances at compile time.

If you want to add lazy loading, enumerations implementing it must be proxies that load the desired object, if requested. The class or classes hidden behind the proxy servers 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; } //} 
+1
source

If you understand correctly, you do not want your class to be created. You can set the default constructor to private

 private Key() throws IllegalStateException //handle default constructor { throw new IllegalStateException(); } 

This will prevent it from being instantiated incorrectly.

Update: Added IllegalStateException Throw

0
source

Source: https://habr.com/ru/post/950818/


All Articles