如何识别Java中的不可变对象

mcherm:

在我的代码中,我正在创建一个对象集合,各个线程将以一种仅在对象是不可变的情况下才安全的方式访问这些对象。当试图将一个新对象插入到我的集合中时,我想测试一下它是否是不可变的(如果不是,我将抛出一个异常)。

我可以做的一件事是检查一些众所周知的不可变类型:

private static final Set<Class> knownImmutables = new HashSet<Class>(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; }
}

是否有某种方式(也许使用反射)可以可靠地检测到类是否不可变?误报(认为不可变时是不可变的)是不能接受的,而误报(认为不可变时则是可变的)是可以接受的。

编辑添加:感谢您的深刻而有益的回答。正如一些答案所指出的那样,我忽略了定义我的安全目标。这里的威胁是无知的开发人员-这是一段框架代码,将被大量几乎不了解线程并且不会阅读文档的人使用。我不需要防御恶意的开发人员,只要有足够聪明的人来诱变String或执行其他恶作剧,他们也将足够聪明,知道在这种情况下这样做并不安全。只要是自动的,就可以对代码库进行静态分析,但是不能依靠代码审阅,因为不能保证每个审阅都会有精通线程的审阅者。

西蒙·莱曼:

没有可靠的方法来检测类是否不可变。这是因为可以通过多种方式更改类的属性,而您无法通过反射来检测所有这些属性。

达到此目的的唯一方法是:

  • 仅允许不可变类型的最终属性(您知道的原始类型和类是不可变的),
  • 要求课程本身是最终的
  • 要求它们从您提供的基类继承(保证是不可变的)

然后,您可以使用以下代码检查您拥有的对象是否不可变:

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不是final!)。另外,您很容易陷入无尽的递归,导致StackOverflowErrors ;)其他问题可能是由泛型引起的,在泛型中还必须检查其类型是否不变。

我认为通过一些工作,这些潜在问题可能会得到解决。但是随后,您必须首先问自己是否真的值得(也是性能明智的选择)。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章