当前位置: 首页 > 面试题库 >

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

琴光亮
2023-03-14
问题内容

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

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

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 ;)其他问题可能是由泛型引起的,在泛型中还必须检查其类型的不可变性。

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



 类似资料:
  • 由于这是一个热门的话题,这些天,我无法理解某些概念。请原谅,如果我听起来很愚蠢,但当我试图创建不可变对象时,我发现大多数帖子如下 null null 此测试用例失败并打印Cassandra。 如果我做错了什么让我知道。

  • 问题内容: 我有以下示例未完成方法来比较给定对象的对象类型 该方法可以称为: 此方法实际上不起作用,请帮助我使其起作用 问题答案: 您忘记了: 请注意,此类代码通常是不良OO设计的标志。 还要注意,将对象的类与类进行比较,并使用instanceof是不一样的。例如: 是假的,而 是真的。 是否必须使用一个或另一个取决于您的要求。

  • 问题内容: 这是Java Concurrency in Practice中的一句话 共享的只读对象包括不可变的和实际上不可变的对象。 不变对象和有效不变对象之间有什么区别? 问题答案: 不可扩展且其字段全部为自身且不可变的类的实例是不可变的。 由于其方法的详细信息而无法更改其字段的类的实例实际上是不可变的。例如: 的某些实例实际上是不可变的,而有些则不是。 另一个例子是零长度数组。它们实际上是不可

  • 问题内容: Java中不变对象的优点显而易见: 一致状态 自动线程安全 简单 您可以通过使用私有final字段和构造函数注入来支持不变性。 但是,在Java中偏爱不可变对象有哪些弊端? 即 与ORM或Web演示工具不兼容? 不灵活的设计? 实施的复杂性? 是否可以设计一个主要使用不可变对象的大规模系统(深层对象图)? 问题答案: 但是,在Java中偏爱不可变对象有哪些弊端?与ORM或Web演示工具

  • 我的目标是让Java对象不可变。我有一个班级< code >学生。为了实现不变性,我用以下方式对它进行了编码: 我的问题是,实现班级不变性的最佳方法是什么?

  • 如何在java脚本中识别/检测循环对象类型? 圆形对象的示例: 如果我们尝试使用JSON字符串化循环对象。stringify(obj),它将抛出一个错误,如下所示 在JSON. stringify()将循环结构转换为JSON