在我的代码中,我正在创建一个对象集合,这些对象将以各种线程的方式访问,只有在对象是不可变的情况下才是安全的.当尝试将新对象插入到我的集合中时,我想测试它是否是不可变的(如果没有,我会抛出异常).
我能做的一件事就是检查一些众所周知的不可变类型:
private static final SetknownImmutables = new HashSet (Arrays.asList( String.class, Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Boolean.class, BigInteger.class, BigDecimal.class )); ... public static boolean isImmutable(Object o) { return knownImmutables.contains(o.getClass()); }
这实际上让我90%的方式,但有时我的用户会想要创建自己的简单不可变类型:
public class ImmutableRectangle { private final int width; private final int height; public ImmutableRectangle(int width, int height) { this.width = width; this.height = height; } public int getWidth() { return width; } public int getHeight() { return height; } }
是否有某种方式(可能使用反射)我可以可靠地检测一个类是否是不可变的?假阳性(认为它不是不可变的)是不可接受的,但是假阴性(认为它是可变的,当它不是)时.
编辑补充:感谢有见地和有帮助的答案.正如一些答案所指出的那样,我忽略了我的安全目标.这里的威胁是无能为力的开发人员 - 这是一个框架代码,将被大量知道线程无关的人使用,不会阅读文档.我不需要为恶意开发者辩护 - 任何聪明到足以变异字符串或执行其他恶作剧的人都会足够聪明地知道它在这种情况下不安全.代码库的静态分析是一种选择,只要它是自动化的,但代码审查不能被依赖,因为不能保证每次审查都会有线程精通的审阅者.
没有可靠的方法来检测类是否是不可变的.这是因为有很多方法可以改变类的属性,并且无法通过反射检测所有属性.
接近这一点的唯一方法是:
只允许不可变类型的最终属性(原始类型和你知道的类是不可变的),
要求班级本身是最终的
要求它们从您提供的基类继承(保证不可变)
然后,如果您拥有的对象是不可变的,则可以使用以下代码进行检查:
static boolean isImmutable(Object obj) { Class> objClass = obj.getClass(); // Class of the object must be a direct child class of the required class Class> superClass = objClass.getSuperclass(); if (!Immutable.class.equals(superClass)) { return false; } // Class must be final if (!Modifier.isFinal(objClass.getModifiers())) { return false; } // Check all fields defined in the class for type and if they are final Field[] objFields = objClass.getDeclaredFields(); for (int i = 0; i < objFields.length; i++) { if (!Modifier.isFinal(objFields[i].getModifiers()) || !isValidFieldType(objFields[i].getType())) { return false; } } // Lets hope we didn't forget something return true; } static boolean isValidFieldType(Class> type) { // Check for all allowed property types... return type.isPrimitive() || String.class.equals(type); }
更新:正如评论中所建议的那样,它可以扩展到递归超类而不是检查某个类.还建议在isValidFieldType方法中递归使用isImmutable.这可能有用,我也做了一些测试.但这不是微不足道的.您不能只通过调用isImmutable来检查所有字段类型,因为String已经通过了此测试(其字段hash
不是最终的!).此外,您很容易遇到无休止的递归,导致StackOverflowErrors ;)其他问题可能是由泛型引起的,您还必须检查其类型的不可变性.
我认为通过一些工作,这些潜在的问题可能会以某种方式得到解决.但是,你必须首先问自己是否真的值得(也是表现明智).
使用Java Concurrency in Practice中的Immutable注释.然后,工具FindBugs可以帮助检测可变但不应该是的类.
在我的公司,我们定义了一个名为的属性@Immutable
.如果你选择将它附加到一个类,这意味着你保证你是不可变的.
它适用于文档,在您的情况下,它将作为过滤器.
当然,你仍然依赖于作者保持他的关于不可变的话,但由于作者明确地添加了注释,这是一个合理的假设.
基本上没有.
你可以构建一个巨大的已接受类的白名单,但我认为不那么疯狂的方法就是在集合的文档中写入所有内容,这个集合必须是不可变的.
编辑:其他人建议使用不可变注释.这很好,但您也需要文档.否则人们会认为"如果我把这个注释放在我的课堂上,我可以将它存储在集合中",并且只会把它放在任何东西,不可变和可变的类上.事实上,我会谨慎地拥有一个不可变的注释,以防人们认为注释使他们的类不可变.