我有一些类存储包含重要信息的键。没有其他人允许来创建密钥,因为密钥依赖于静态信息(如某些目录结构等)。
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;
}
}
}
这个例子很简单。
我想测试错误的键(异常处理等)的后果,并通过JUnit测试用例中的反射实例化该类。
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);
然后我问自己如何防止进一步实例化密钥类(即阻止通过反射进一步创建对象)?
enums
出现在我的脑海中,但它们不适用于泛型。
public enum Key<T>
{
//... Syntax error, enum declaration cannot have type parameters
}
那么如何保留一组泛型类的n
个实例并防止进一步实例化?
答案 0 :(得分:3)
那么如何保留一组泛型类的n个实例并防止 进一步实例化?
如果您真的想要使用此模式,那么任何人(包括您)都不应该能够实例化Key
对象。为了在具有此模式的类中保留一组n个实例,您可以使用私有构造函数,访问静态方法和SecurityManager来防止反射。既然你希望能够作为公共常量访问密钥,我会尝试这样的东西..
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;
}
}
}
使用SecurityManager
阻止反射访问。
//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");
}
}
只要有人试图访问您班级中的私有变量或字段(包括通过反射进行访问尝试),就会抛出SecurityException
。
答案 1 :(得分:1)
我不确定我是否完全理解你的问题,但如果私有构造函数不够,你可以使用更动态的方法并在给出信号后在构造函数中抛出异常吗?例如:
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;
}
}
然后 - 这就是缺点 - 一旦你想阻止创建更多实例,就必须调用Key.lock()
。
答案 2 :(得分:1)
正如您在代码中展示的那样,为了防止实例化KeyConstants
,您可以在private-non-argument构造函数中抛出一些Exception。
更难的部分是阻止从KeyConstants
类外部创建KeyConstants.Key构造函数的方法。
也许在构造函数中创建Exception并检查其堆栈跟踪的样子。当我将此代码添加到构造函数
时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;
}
并使用反射创建Key的实例,如
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);
我得到了
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)
所以也许只是堆栈的第4个元素是java.lang.reflect.Constructor.newInstance
抛出异常以防止执行其余的构造函数代码,如:
if (stack.length>=4 && stack[4].toString().startsWith("java.lang.reflect.Constructor.newInstance")){
throw new RuntimeException("cant create object with reflection");
}
答案 3 :(得分:1)
我最近遇到了一些Multiton模式,我尝试使用独特的枚举键处理问题,这让我想到了另一种方法。
密钥可用于我想要的信息流,甚至可以用作类型安全异构容器的密钥,它们可以执行编译时转换。
键定义类
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;
}
}
}
使用密钥的示例
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
}
}
答案 4 :(得分:1)
我有另一种方法,您可以通过enum
实现的方式绑定接口。
使用这种方法,您可以在编译时获得一组固定的实例。
如果要添加延迟加载,实现它的枚举应该是在请求时加载所需对象的代理。隐藏在代理服务器后面的一个或多个类应该只对它们可见,这样它们才能拥有对构造函数的独占访问权。
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; }
//}
答案 5 :(得分:0)
如果我理解正确,你不希望你的类被实例化。 您可以将默认构造函数设置为private
private Key() throws IllegalStateException //handle default constructor
{
throw new IllegalStateException();
}
这将阻止其不正确的实例化。
更新: 添加了throw IllegalStateException