如何识别Java中的不可变对象 [英] How do I identify immutable objects in Java

查看:96
本文介绍了如何识别Java中的不可变对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

  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。类,BigInteger.class,BigDecimal.class
));

...

public static boolean isImmutable(Object o){
return knownImmutables.contains(o.getClass());
}

这实际上使我获得了90%的收益,但有时我的用户会希望创建自己的简单不可变类型:

pre $ 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<?> superClass = objClass.getSuperclass();
if(!Immutable.class.equals(superClass)){
return false;
}

//类必须是final b $ b if(!Modifier.isFinal(objClass.getModifiers())){
return false;
}

//检查类中定义的所有字段的类型,如果它们是最终的
Field [] objFields = objClass.getDeclaredFields();
for(int i = 0; i< objFields.length; i ++){
if(!Modifier.isFinal(objFields [i] .getModifiers())
||!isValidFieldType( objFields [i] .getType())){
return false;
}
}

//让我们希望我们不会忘记某件事
return true;

$ b $静态布尔isValidFieldType(Class<> type){
//检查所有允许的属性类型...
返回type.isPrimitive() || String.class.equals(类型);
}

更新:可以扩展到超类的递归而不是检查某个类。还建议在isValidFieldType方法中递归使用isImmutable。这可能工作,我也做了一些测试。但这不是微不足道的。您不能仅仅通过调用isImmutable来检查所有字段类型,因为String已经失败了此测试(其字段 hash 不是最终的!)。你也很容易陷入无尽的递归中,导致 StackOverflowErrors ;)其他问题可能是由泛型引起的,你也必须检查它们的类型是否可变。



我认为在一些工作中,这些潜在的问题可能会以某种方式解决。但是,你必须首先问自己是否真的值得(也是表现明智的)。

In my code, I am creating a collection of objects which will be accessed by various threads in a fashion that is only safe if the objects are immutable. When an attempt is made to insert a new object into my collection, I want to test to see if it is immutable (if not, I'll throw an exception).

One thing I can do is to check a few well-known immutable types:

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());
}

This actually gets me 90% of the way, but sometimes my users will want to create simple immutable types of their own:

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; }
}

Is there some way (perhaps using reflection) that I could reliably detect whether a class is immutable? False positives (thinking it's immutable when it isn't) are not acceptable but false negatives (thinking it's mutable when it isn't) are.

Edited to add: Thanks for the insightful and helpful answers. As some of the answers pointed out, I neglected to define my security objectives. The threat here is clueless developers -- this is a piece of framework code that will be used by large numbers of people who know next-to-nothing about threading and won't be reading the documentation. I do NOT need to defend against malicious developers -- anyone clever enough to mutate a String or perform other shenanigans will also be smart enough to know it's not safe in this case. Static analysis of the codebase IS an option, so long as it is automated, but code reviews cannot be counted on because there is no guarantee every review will have threading-savvy reviewers.

解决方案

There is no reliable way to detect if a class is immutable. This is because there are so many ways a property of a class might be altered and you can't detect all of them via reflection.

The only way to get close to this is:

  • Only allow final properties of types that are immutable (primitive types and classes you know are immutable),
  • Require the class to be final itself
  • Require that they inherit from a base class you provide (which is guaranteed to be immutable)

Then you can check with the following code if the object you have is immutable:

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);
}

Update: As suggested in the comments, it could be extended to recurse on the superclass instead of checking for a certain class. It was also suggested to recursively use isImmutable in the isValidFieldType Method. This could probably work and I have also done some testing. But this is not trivial. You can't just check all field types with a call to isImmutable, because String already fails this test (its field hash is not final!). Also you are easily running into endless recursions, causing StackOverflowErrors ;) Other problems might be caused by generics, where you also have to check their types for immutablity.

I think with some work, these potential problems might be solved somehow. But then, you have to ask yourself first if it really is worth it (also performance wise).

这篇关于如何识别Java中的不可变对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆