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

时间:2022-01-10 22:04:48

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:

这实际上让我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; }
}

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.

编辑补充:感谢有见地和有帮助的答案。正如一些答案所指出的那样,我忽略了我的安全目标。这里的威胁是无能为力的开发人员 - 这是一个框架代码,将被大量知道线程无关的人使用,不会阅读文档。我不需要为恶意开发者辩护 - 任何聪明到足以改变字符串或执行其他恶作剧的人都会足够聪明,知道在这种情况下它不安全。代码库的静态分析是一种选择,只要它是自动化的,但代码审查不能被依赖,因为不能保证每次审查都会有线程精通的审阅者。

15 个解决方案

#1


29  

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 *Errors ;) Other problems might be caused by generics, where you also have to check their types for immutablity.

更新:正如评论中所建议的那样,它可以扩展到递归超类而不是检查某个类。还建议在isValidFieldType方法中递归使用isImmutable。这可能有用,我也做了一些测试。但这不是微不足道的。您不能只通过调用isImmutable来检查所有字段类型,因为String已经通过了此测试(其字段哈希不是最终的!)。此外,您很容易遇到无休止的递归,导致*Errors;)其他问题可能是由泛型引起的,您还必须检查其类型的不可变性。

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).

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

#2


29  

Use the Immutable annotation from Java Concurrency in Practice. The tool FindBugs can then help in detecting classes which are mutable but shouldn't be.

使用Java Concurrency in Practice中的Immutable注释。然后,FindBugs工具可以帮助检测可变但不应该是的类。

#3


9  

At my company we've defined an Attribute called @Immutable. If you choose to attach that to a class, it means you promise you're immutable.

在我的公司,我们定义了一个名为@Immutable的属性。如果你选择将它附加到一个类,这意味着你保证你是不可变的。

It works for documentation, and in your case it would work as a filter.

它适用于文档,在您的情况下,它将作为过滤器。

Of course you're still depending on the author keeping his word about being immutable, but since the author explicitly added the annotation it's a reasonable assumption.

当然,你仍然依赖于作者保持他的关于不可变的说法,但由于作者明确地添加了注释,这是一个合理的假设。

#4


8  

Basically no.

基本上没有。

You could build a giant white-list of accepted classes but I think the less crazy way would be to just write in the documentation for the collection that everything that goes is this collection must be immutable.

你可以构建一个巨大的已接受类的白名单,但我认为不那么疯狂的方法就是在集合的文档中写入所有内容,这个集合必须是不可变的。

Edit: Other people have suggested having an immutable annotation. This is fine, but you need the documentation as well. Otherwise people will just think "if I put this annotation on my class I can store it in the collection" and will just chuck it on anything, immutable and mutable classes alike. In fact, I would be wary of having an immutable annotation just in case people think that annotation makes their class immutable.

编辑:其他人建议使用不可变注释。这很好,但您也需要文档。否则人们会认为“如果我把这个注释放在我的课上,我可以将它存储在集合中”,并且只会把它放在任何东西,不可变和可变的类上。事实上,我会谨慎地拥有一个不可变的注释,以防人们认为注释使他们的类不可变。

#5


4  

This could be another hint:

这可能是另一个提示:

If the class has no setters then it cannot be mutated, granted the parameters it was created with are either "primitive" types or not mutable themselves.

如果该类没有setter,则它不能被变异,授予它所创建的参数是“原始”类型或者本身不可变。

Also no methods could be overriden, all fields are final and private,

也没有方法可以覆盖,所有字段都是final和private,

I'll try to code something tomorrow for you, but Simon's code using reflection looks pretty good.

我会尝试为你明天编写代码,但Simon使用反射的代码看起来非常好。

In the mean time try to grab a copy of the "Effective Java" book by Josh Block , it has an Item related to this topic. While is does not for sure say how to detect an inmmutable class, it shows how to create a good one.

同时尝试获取Josh Block的“Effective Java”一书的副本,它有一个与此主题相关的项目。虽然不能确定如何检测一个不可改变的类,但它显示了如何创建一个好的类。

The item is called: "Favor immutability"

这个项目被称为:“支持不变性”

link: http://java.sun.com/docs/books/effective/

链接:http://java.sun.com/docs/books/effective/

#6


4  

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.

在我的代码中,我正在创建一个对象集合,这些对象将以各种线程的方式访问,只有在对象是不可变的情况下才是安全的。

Not a direct answer to your question, but keep in mind that objects that are immutable are not automatically guaranteed to be thread safe (sadly). Code needs to be side-effect free to be thread safe, and that's quite a bit more difficult.

不是你的问题的直接答案,但请记住,不可变的对象不会自动保证是线程安全的(遗憾的是)。代码需要是副作用免费的线程安全,这是非常困难的。

Suppose you have this class:

假设你有这个类:

class Foo {
  final String x;
  final Integer y;
  ...

  public bar() {
    Singleton.getInstance().foolAround();
  }
}

Then the foolAround() method might include some non-thread safe operations, which will blow up your app. And it's not possible to test for this using reflection, as the actual reference can only be found in the method body, not in the fields or exposed interface.

然后foolAround()方法可能包含一些非线程安全操作,这将使您的应用程序爆炸。并且不可能使用反射来测试它,因为实际引用只能在方法体中找到,而不能在字段或暴露的接口中找到。

Other than that, the others are correct: you can scan for all declared fields of the class, check if every one of them is final and also an immutable class, and you're done. I don't think methods being final is a requirement.

除此之外,其他的都是正确的:你可以扫描类的所有声明的字段,检查它们中的每一个是最终的还是一个不可变的类,你就完成了。我认为方法不是最终的要求。

Also, be careful about recursively checking dependent fields for immutability, you might end up with circles:

另外,要小心递归检查依赖字段的不变性,最后可能会得到圆圈:

class A {
  final B b; // might be immutable...
}

class B {
  final A a; // same so here.
}

Classes A and B are perfectly immutable (and possibly even usable through some reflection hacks), but naive recursive code will go into an endless loop checking A, then B, then A again, onwards to B, ...

类A和B是完全不可变的(甚至可能通过一些反射黑客可以使用),但天真的递归代码将进入无限循环检查A,然后是B,然后再次A,再到B,......

You can fix that with a 'seen' map that disallows cycles, or with some really clever code that decides classes are immutable if all their dependees are immutable only depending on themselves, but that's going to be really complicated...

您可以通过一个不允许循环的“看到”映射来修复它,或者使用一些非常聪明的代码来决定类是不可变的,如果它们的所有依赖项都是不可变的,只取决于它们自己,但这将是非常复杂的......

#7


3  

You Can Ask your clients to add metadata (annotations) and check them at runtime with reflection, like this:

您可以要求您的客户添加元数据(注释)并在运行时使用反射检查它们,如下所示:

Metadata:

元数据:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CLASS)
public @interface Immutable{ }

Client Code:

客户代码:

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

Then by using reflection on the class, check if it has the annotation (I would paste the code but its boilerplate and can be found easily online)

然后通过在类上使用反射,检查它是否有注释(我会粘贴代码,但它的样板,可以在网上轻松找到)

#8


3  

why do all the recommendations require the class to be final? if you are using reflection to check the class of each object, and you can determine programmatically that that class is immutable (immutable, final fields), then you don't need to require that the class itself is final.

为什么所有建议都要求全班最终?如果您使用反射来检查每个对象的类,并且您可以通过编程方式确定该类是不可变的(不可变的,最终字段),那么您不需要要求类本身是最终的。

#9


3  

You can use AOP and @Immutable annotation from jcabi-aspects:

您可以使用jcabi-aspects中的AOP和@Immutable注释:

@Immutable
public class Foo {
  private String data;
}
// this line will throw a runtime exception since class Foo
// is actually mutable, despite the annotation
Object object = new Foo();

#10


2  

Like the other answerers already said, IMHO there is no reliable way to find out if an object is really immutable.

就像已经说过的其他回答者一样,恕我直言,没有可靠的方法来确定对象是否真的是不可变的。

I would just introduce an interface "Immutable" to check against when appending. This works as a hint that only immutable objects should be inserted for whatever reason you're doing it.

我只是介绍一个“Immutable”接口,以便在追加时进行检查。这可以作为一个提示,只有在你做这个的时候才应该插入不可变对象。

interface Immutable {}

class MyImmutable implements Immutable{...}

public void add(Object o) {
  if (!(o instanceof Immutable) && !checkIsImmutableBasePrimitive(o))
    throw new IllegalArgumentException("o is not immutable!");
  ...
}

#11


1  

Try this:

尝试这个:

public static boolean isImmutable(Object object){
    if (object instanceof Number) { // Numbers are immutable
        if (object instanceof AtomicInteger) {
            // AtomicIntegers are mutable
        } else if (object instanceof AtomicLong) {
            // AtomLongs are mutable
        } else {
            return true;
        }
    } else if (object instanceof String) {  // Strings are immutable
        return true;
    } else if (object instanceof Character) {   // Characters are immutable
        return true;
    } else if (object instanceof Class) { // Classes are immutable
        return true;
    }

    Class<?> objClass = object.getClass();

    // 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())
                            || !isImmutable(objFields[i].getType())) {
                    return false;
            }
    }

    // Lets hope we didn't forget something
    return true;
}

#12


1  

To my knowledge, there is no way to identify immutable objects that is 100% correct. However, I have written a library to get you closer. It performs analysis of bytecode of a class to determine if it is immutable or not, and can execute at runtime. It is on the strict side, so it also allows whitelisting known immutable classes.

据我所知,没有办法识别100%正确的不可变对象。但是,我写了一个图书馆,让你更接近。它执行类的字节码分析以确定它是否是不可变的,并且可以在运行时执行。它是严格的一面,所以它也允许将已知的不可变类列入白名单。

You can check it out at: www.mutabilitydetector.org

您可以访问:www.mutabilitydetector.org查看

It allows you to write code like this in your application:

它允许您在应用程序中编写这样的代码:

/*
* Request an analysis of the runtime class, to discover if this
* instance will be immutable or not.
*/
AnalysisResult result = analysisSession.resultFor(dottedClassName);

if (result.isImmutable.equals(IMMUTABLE)) {
    /*
    * rest safe in the knowledge the class is
    * immutable, share across threads with joyful abandon
    */
} else if (result.isImmutable.equals(NOT_IMMUTABLE)) {
    /*
    * be careful here: make defensive copies,
    * don't publish the reference,
    * read Java Concurrency In Practice right away!
    */
}

It is free and open source under the Apache 2.0 license.

它是Apache 2.0许可下的免费开源软件。

#13


0  

Check out joe-e, a implementation of capabilities for java.

查看joe-e,java的功能实现。

#14


0  

Something which works for a high percentage of builtin classes is test for instanceof Comparable. For the classes which are not immutable like Date, they are often treated as immutable in most cases.

适用于高比例内置类的东西是testof instanceof Comparable。对于像Date一样不可变的类,在大多数情况下它们通常被视为不可变的。

#15


0  

I appreciate and admire the amount of work Grundlefleck has put into his mutability detector, but I think it is a bit of an overkill. You can write a simple but practically very adequate (that is, pragmatic) detector as follows:

我很欣赏和欣赏Grundlefleck在他的可变性检测器中所做的工作量,但我认为这有点过分。您可以编写一个简单但实​​际上非常充足(即实用)的探测器,如下所示:

(note: this is a copy of my comment here: https://*.com/a/28111150/773113)

(注意:这是我的评论副本:https://*.com/a/28111150/773113)

First of all, you are not going to be just writing a method which determines whether a class is immutable; instead, you will need to write an immutability detector class, because it is going to have to maintain some state. The state of the detector will be the detected immutability of all classes which it has examined so far. This is not only useful for performance, but it is actually necessary because a class may contain a circular reference, which would cause a simplistic immutability detector to fall into infinite recursion.

首先,你不会只是编写一个方法来确定一个类是否是不可变的;相反,你需要编写一个不变性检测器类,因为它必须保持一些状态。检测器的状态将是迄今为止检测过的所有类的检测到的不变性。这不仅对性能有用,而且实际上是必要的,因为类可能包含循环引用,这会导致简单的不变性检测器陷入无限递归。

The immutability of a class has four possible values: Unknown, Mutable, Immutable, and Calculating. You will probably want to have a map which associates each class that you have encountered so far to an immutability value. Of course, Unknown does not actually need to be implemented, since it will be the implied state of any class which is not yet in the map.

类的不变性有四个可能的值:Unknown,Mutable,Immutable和Calculating。您可能希望有一个地图,它将您目前遇到的每个类与一个不变性值相关联。当然,Unknown实际上并不需要实现,因为它将是任何尚未在地图中的类的隐含状态。

So, when you begin examining a class, you associate it with a Calculating value in the map, and when you are done, you replace Calculating with either Immutable or Mutable.

因此,当您开始检查类时,将其与地图中的计算值相关联,完成后,将“计算”替换为“可变”或“可变”。

For each class, you only need to check the field members, not the code. The idea of checking bytecode is rather misguided.

对于每个类,您只需要检查字段成员,而不是代码。检查字节码的想法是错误的。

First of all, you should not check whether a class is final; The finality of a class does not affect its immutability. Instead, a method which expects an immutable parameter should first of all invoke the immutability detector to assert the immutability of the class of the actual object that was passed. This test can be omitted if the type of the parameter is a final class, so finality is good for performance, but strictly speaking not necessary. Also, as you will see further down, a field whose type is of a non-final class will cause the declaring class to be considered as mutable, but still, that's a problem of the declaring class, not the problem of the non-final immutable member class. It is perfectly fine to have a tall hierarchy of immutable classes, in which all the non-leaf nodes must of course be non-final.

首先,你不应该检查一个班级是否是最终的;一个阶级的终结性不会影响其不变性。相反,一个期望不可变参数的方法首先应该调用不变性检测器来断言传递的实际对象的类的不变性。如果参数的类型是最终类,则可以省略该测试,因此最终性有利于性能,但严格来说不是必需的。另外,正如你将进一步看到的那样,一个类型为非final类的字段将导致声明类被认为是可变的,但仍然是,这是声明类的问题,而不是非final的问题不可变成员类。拥有一个不可变类的高层次结构是完全正确的,其中所有非叶节点当然必须是非final的。

You should not check whether a field is private; it is perfectly fine for a class to have a public field, and the visibility of the field does not affect the immutability of the declaring class in any way, shape, or form. You only need to check whether the field is final and its type is immutable.

你不应该检查一个字段是否是私有的;对于一个拥有公共领域的类来说,这是完全正确的,并且该领域的可见性不会以任何方式,形状或形式影响声明类的不变性。您只需要检查该字段是否为final,其类型是不可变的。

When examining a class, what you want to do first of all is to recurse to determine the immutability of its super class. If the super is mutable, then the descendant is by definition mutable too.

在检查一个类时,首先要做的是递归以确定其超类的不变性。如果super是可变的,那么后代也是可变的。

Then, you only need to check the declared fields of the class, not all fields.

然后,您只需要检查类的声明字段,而不是所有字段。

If a field is non-final, then your class is mutable.

如果一个字段是非final字段,那么你的类是可变的。

If a field is final, but the type of the field is mutable, then your class is mutable. (Arrays are by definition mutable.)

如果一个字段是final,但字段的类型是可变的,那么你的类是可变的。 (根据定义,数组是可变的。)

If a field is final, and the type of the field is Calculating, then ignore it and proceed to the next field. If all fields are either immutable or Calculating, then your class is immutable.

如果字段是final,并且字段的类型是Calculating,则忽略它并继续下一个字段。如果所有字段都是不可变的或计算,那么您的类是不可变的。

If the type of the field is an interface, or an abstract class, or a non-final class, then it is to be considered as mutable, since you have absolutely no control over what the actual implementation may do. This might seem like an insurmountable problem, because it means that wrapping a modifiable collection inside an UnmodifiableCollection will still fail the immutability test, but it is actually fine, and it can be handled with the following workaround.

如果字段的类型是接口,抽象类或非final类,那么它将被视为可变,因为您完全无法控制实际实现可能执行的操作。这似乎是一个不可克服的问题,因为这意味着在UnmodifiableCollection中包装一个可修改的集合仍然会使不变性测试失败,但它实际上很好,并且可以通过以下解决方法来处理它。

Some classes may contain non-final fields and still be effectively immutable. An example of this is the String class. Other classes which fall into this category are classes which contain non-final members purely for performance monitoring purposes (invocation counters, etc.), classes which implement popsicle immutability (look it up), and classes which contain members that are interfaces which are known to not cause any side effects. Also, if a class contains bona fide mutable fields but promises not to take them into account when computing hashCode() and equals(), then the class is of course unsafe when it comes to multi-threading, but it can still be considered as immutable for the purpose of using it as a key in a map. So, all these cases can be handled in one of two ways:

某些类可能包含非final字段,但仍然是有效的不可变字段。一个例子是String类。属于此类别的其他类是包含纯粹用于性能监视目的的非最终成员(调用计数器等)的类,实现冰棒不变性的类(查找它),以及包含已知接口的成员的类不会引起任何副作用。此外,如果一个类包含真正的可变字段但承诺在计算hashCode()和equals()时不考虑它们,那么当涉及到多线程时,该类当然是不安全的,但它仍然可以被认为是为了将它用作地图中的键而不可变。因此,所有这些情况都可以通过以下两种方式之一来处理:

  1. Manually adding classes (and interfaces) to your immutability detector. If you know that a certain class is effectively immutable despite the fact that the immutability test for it fails, you can manually add an entry to your detector which associates it with Immutable. This way, the detector will never attempt to check whether it is immutable, it will always just say 'yes, it is.'

    手动将类(和接口)添加到不变性检测器。如果您知道某个类实际上是不可变的,尽管它的不变性测试失败了,您可以手动向检测器添加一个条目,将其与Immutable相关联。这样,探测器永远不会尝试检查它是否是不可变的,它总是只是说'是,它是'。

  2. Introducing an @ImmutabilityOverride annotation. Your immutability detector can check for the presence of this annotation on a field, and if present, it may treat the field as immutable despite the fact that the field may be non-final or its type may be mutable. The detector may also check for the presence of this annotation on the class, thus treating the class as immutable without even bothering to check its fields.

    介绍@ImmutabilityOverride注释。您的不变性检测器可以检查字段上是否存在此注释,如果存在,它可以将该字段视为不可变字段,尽管字段可能是非最终字段或其类型可能是可变的。检测器还可以检查类上是否存在此注释,从而将类视为不可变,甚至不必检查其字段。

I hope this helps future generations.

我希望这有助于后代。

#1


29  

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 *Errors ;) Other problems might be caused by generics, where you also have to check their types for immutablity.

更新:正如评论中所建议的那样,它可以扩展到递归超类而不是检查某个类。还建议在isValidFieldType方法中递归使用isImmutable。这可能有用,我也做了一些测试。但这不是微不足道的。您不能只通过调用isImmutable来检查所有字段类型,因为String已经通过了此测试(其字段哈希不是最终的!)。此外,您很容易遇到无休止的递归,导致*Errors;)其他问题可能是由泛型引起的,您还必须检查其类型的不可变性。

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).

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

#2


29  

Use the Immutable annotation from Java Concurrency in Practice. The tool FindBugs can then help in detecting classes which are mutable but shouldn't be.

使用Java Concurrency in Practice中的Immutable注释。然后,FindBugs工具可以帮助检测可变但不应该是的类。

#3


9  

At my company we've defined an Attribute called @Immutable. If you choose to attach that to a class, it means you promise you're immutable.

在我的公司,我们定义了一个名为@Immutable的属性。如果你选择将它附加到一个类,这意味着你保证你是不可变的。

It works for documentation, and in your case it would work as a filter.

它适用于文档,在您的情况下,它将作为过滤器。

Of course you're still depending on the author keeping his word about being immutable, but since the author explicitly added the annotation it's a reasonable assumption.

当然,你仍然依赖于作者保持他的关于不可变的说法,但由于作者明确地添加了注释,这是一个合理的假设。

#4


8  

Basically no.

基本上没有。

You could build a giant white-list of accepted classes but I think the less crazy way would be to just write in the documentation for the collection that everything that goes is this collection must be immutable.

你可以构建一个巨大的已接受类的白名单,但我认为不那么疯狂的方法就是在集合的文档中写入所有内容,这个集合必须是不可变的。

Edit: Other people have suggested having an immutable annotation. This is fine, but you need the documentation as well. Otherwise people will just think "if I put this annotation on my class I can store it in the collection" and will just chuck it on anything, immutable and mutable classes alike. In fact, I would be wary of having an immutable annotation just in case people think that annotation makes their class immutable.

编辑:其他人建议使用不可变注释。这很好,但您也需要文档。否则人们会认为“如果我把这个注释放在我的课上,我可以将它存储在集合中”,并且只会把它放在任何东西,不可变和可变的类上。事实上,我会谨慎地拥有一个不可变的注释,以防人们认为注释使他们的类不可变。

#5


4  

This could be another hint:

这可能是另一个提示:

If the class has no setters then it cannot be mutated, granted the parameters it was created with are either "primitive" types or not mutable themselves.

如果该类没有setter,则它不能被变异,授予它所创建的参数是“原始”类型或者本身不可变。

Also no methods could be overriden, all fields are final and private,

也没有方法可以覆盖,所有字段都是final和private,

I'll try to code something tomorrow for you, but Simon's code using reflection looks pretty good.

我会尝试为你明天编写代码,但Simon使用反射的代码看起来非常好。

In the mean time try to grab a copy of the "Effective Java" book by Josh Block , it has an Item related to this topic. While is does not for sure say how to detect an inmmutable class, it shows how to create a good one.

同时尝试获取Josh Block的“Effective Java”一书的副本,它有一个与此主题相关的项目。虽然不能确定如何检测一个不可改变的类,但它显示了如何创建一个好的类。

The item is called: "Favor immutability"

这个项目被称为:“支持不变性”

link: http://java.sun.com/docs/books/effective/

链接:http://java.sun.com/docs/books/effective/

#6


4  

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.

在我的代码中,我正在创建一个对象集合,这些对象将以各种线程的方式访问,只有在对象是不可变的情况下才是安全的。

Not a direct answer to your question, but keep in mind that objects that are immutable are not automatically guaranteed to be thread safe (sadly). Code needs to be side-effect free to be thread safe, and that's quite a bit more difficult.

不是你的问题的直接答案,但请记住,不可变的对象不会自动保证是线程安全的(遗憾的是)。代码需要是副作用免费的线程安全,这是非常困难的。

Suppose you have this class:

假设你有这个类:

class Foo {
  final String x;
  final Integer y;
  ...

  public bar() {
    Singleton.getInstance().foolAround();
  }
}

Then the foolAround() method might include some non-thread safe operations, which will blow up your app. And it's not possible to test for this using reflection, as the actual reference can only be found in the method body, not in the fields or exposed interface.

然后foolAround()方法可能包含一些非线程安全操作,这将使您的应用程序爆炸。并且不可能使用反射来测试它,因为实际引用只能在方法体中找到,而不能在字段或暴露的接口中找到。

Other than that, the others are correct: you can scan for all declared fields of the class, check if every one of them is final and also an immutable class, and you're done. I don't think methods being final is a requirement.

除此之外,其他的都是正确的:你可以扫描类的所有声明的字段,检查它们中的每一个是最终的还是一个不可变的类,你就完成了。我认为方法不是最终的要求。

Also, be careful about recursively checking dependent fields for immutability, you might end up with circles:

另外,要小心递归检查依赖字段的不变性,最后可能会得到圆圈:

class A {
  final B b; // might be immutable...
}

class B {
  final A a; // same so here.
}

Classes A and B are perfectly immutable (and possibly even usable through some reflection hacks), but naive recursive code will go into an endless loop checking A, then B, then A again, onwards to B, ...

类A和B是完全不可变的(甚至可能通过一些反射黑客可以使用),但天真的递归代码将进入无限循环检查A,然后是B,然后再次A,再到B,......

You can fix that with a 'seen' map that disallows cycles, or with some really clever code that decides classes are immutable if all their dependees are immutable only depending on themselves, but that's going to be really complicated...

您可以通过一个不允许循环的“看到”映射来修复它,或者使用一些非常聪明的代码来决定类是不可变的,如果它们的所有依赖项都是不可变的,只取决于它们自己,但这将是非常复杂的......

#7


3  

You Can Ask your clients to add metadata (annotations) and check them at runtime with reflection, like this:

您可以要求您的客户添加元数据(注释)并在运行时使用反射检查它们,如下所示:

Metadata:

元数据:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CLASS)
public @interface Immutable{ }

Client Code:

客户代码:

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

Then by using reflection on the class, check if it has the annotation (I would paste the code but its boilerplate and can be found easily online)

然后通过在类上使用反射,检查它是否有注释(我会粘贴代码,但它的样板,可以在网上轻松找到)

#8


3  

why do all the recommendations require the class to be final? if you are using reflection to check the class of each object, and you can determine programmatically that that class is immutable (immutable, final fields), then you don't need to require that the class itself is final.

为什么所有建议都要求全班最终?如果您使用反射来检查每个对象的类,并且您可以通过编程方式确定该类是不可变的(不可变的,最终字段),那么您不需要要求类本身是最终的。

#9


3  

You can use AOP and @Immutable annotation from jcabi-aspects:

您可以使用jcabi-aspects中的AOP和@Immutable注释:

@Immutable
public class Foo {
  private String data;
}
// this line will throw a runtime exception since class Foo
// is actually mutable, despite the annotation
Object object = new Foo();

#10


2  

Like the other answerers already said, IMHO there is no reliable way to find out if an object is really immutable.

就像已经说过的其他回答者一样,恕我直言,没有可靠的方法来确定对象是否真的是不可变的。

I would just introduce an interface "Immutable" to check against when appending. This works as a hint that only immutable objects should be inserted for whatever reason you're doing it.

我只是介绍一个“Immutable”接口,以便在追加时进行检查。这可以作为一个提示,只有在你做这个的时候才应该插入不可变对象。

interface Immutable {}

class MyImmutable implements Immutable{...}

public void add(Object o) {
  if (!(o instanceof Immutable) && !checkIsImmutableBasePrimitive(o))
    throw new IllegalArgumentException("o is not immutable!");
  ...
}

#11


1  

Try this:

尝试这个:

public static boolean isImmutable(Object object){
    if (object instanceof Number) { // Numbers are immutable
        if (object instanceof AtomicInteger) {
            // AtomicIntegers are mutable
        } else if (object instanceof AtomicLong) {
            // AtomLongs are mutable
        } else {
            return true;
        }
    } else if (object instanceof String) {  // Strings are immutable
        return true;
    } else if (object instanceof Character) {   // Characters are immutable
        return true;
    } else if (object instanceof Class) { // Classes are immutable
        return true;
    }

    Class<?> objClass = object.getClass();

    // 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())
                            || !isImmutable(objFields[i].getType())) {
                    return false;
            }
    }

    // Lets hope we didn't forget something
    return true;
}

#12


1  

To my knowledge, there is no way to identify immutable objects that is 100% correct. However, I have written a library to get you closer. It performs analysis of bytecode of a class to determine if it is immutable or not, and can execute at runtime. It is on the strict side, so it also allows whitelisting known immutable classes.

据我所知,没有办法识别100%正确的不可变对象。但是,我写了一个图书馆,让你更接近。它执行类的字节码分析以确定它是否是不可变的,并且可以在运行时执行。它是严格的一面,所以它也允许将已知的不可变类列入白名单。

You can check it out at: www.mutabilitydetector.org

您可以访问:www.mutabilitydetector.org查看

It allows you to write code like this in your application:

它允许您在应用程序中编写这样的代码:

/*
* Request an analysis of the runtime class, to discover if this
* instance will be immutable or not.
*/
AnalysisResult result = analysisSession.resultFor(dottedClassName);

if (result.isImmutable.equals(IMMUTABLE)) {
    /*
    * rest safe in the knowledge the class is
    * immutable, share across threads with joyful abandon
    */
} else if (result.isImmutable.equals(NOT_IMMUTABLE)) {
    /*
    * be careful here: make defensive copies,
    * don't publish the reference,
    * read Java Concurrency In Practice right away!
    */
}

It is free and open source under the Apache 2.0 license.

它是Apache 2.0许可下的免费开源软件。

#13


0  

Check out joe-e, a implementation of capabilities for java.

查看joe-e,java的功能实现。

#14


0  

Something which works for a high percentage of builtin classes is test for instanceof Comparable. For the classes which are not immutable like Date, they are often treated as immutable in most cases.

适用于高比例内置类的东西是testof instanceof Comparable。对于像Date一样不可变的类,在大多数情况下它们通常被视为不可变的。

#15


0  

I appreciate and admire the amount of work Grundlefleck has put into his mutability detector, but I think it is a bit of an overkill. You can write a simple but practically very adequate (that is, pragmatic) detector as follows:

我很欣赏和欣赏Grundlefleck在他的可变性检测器中所做的工作量,但我认为这有点过分。您可以编写一个简单但实​​际上非常充足(即实用)的探测器,如下所示:

(note: this is a copy of my comment here: https://*.com/a/28111150/773113)

(注意:这是我的评论副本:https://*.com/a/28111150/773113)

First of all, you are not going to be just writing a method which determines whether a class is immutable; instead, you will need to write an immutability detector class, because it is going to have to maintain some state. The state of the detector will be the detected immutability of all classes which it has examined so far. This is not only useful for performance, but it is actually necessary because a class may contain a circular reference, which would cause a simplistic immutability detector to fall into infinite recursion.

首先,你不会只是编写一个方法来确定一个类是否是不可变的;相反,你需要编写一个不变性检测器类,因为它必须保持一些状态。检测器的状态将是迄今为止检测过的所有类的检测到的不变性。这不仅对性能有用,而且实际上是必要的,因为类可能包含循环引用,这会导致简单的不变性检测器陷入无限递归。

The immutability of a class has four possible values: Unknown, Mutable, Immutable, and Calculating. You will probably want to have a map which associates each class that you have encountered so far to an immutability value. Of course, Unknown does not actually need to be implemented, since it will be the implied state of any class which is not yet in the map.

类的不变性有四个可能的值:Unknown,Mutable,Immutable和Calculating。您可能希望有一个地图,它将您目前遇到的每个类与一个不变性值相关联。当然,Unknown实际上并不需要实现,因为它将是任何尚未在地图中的类的隐含状态。

So, when you begin examining a class, you associate it with a Calculating value in the map, and when you are done, you replace Calculating with either Immutable or Mutable.

因此,当您开始检查类时,将其与地图中的计算值相关联,完成后,将“计算”替换为“可变”或“可变”。

For each class, you only need to check the field members, not the code. The idea of checking bytecode is rather misguided.

对于每个类,您只需要检查字段成员,而不是代码。检查字节码的想法是错误的。

First of all, you should not check whether a class is final; The finality of a class does not affect its immutability. Instead, a method which expects an immutable parameter should first of all invoke the immutability detector to assert the immutability of the class of the actual object that was passed. This test can be omitted if the type of the parameter is a final class, so finality is good for performance, but strictly speaking not necessary. Also, as you will see further down, a field whose type is of a non-final class will cause the declaring class to be considered as mutable, but still, that's a problem of the declaring class, not the problem of the non-final immutable member class. It is perfectly fine to have a tall hierarchy of immutable classes, in which all the non-leaf nodes must of course be non-final.

首先,你不应该检查一个班级是否是最终的;一个阶级的终结性不会影响其不变性。相反,一个期望不可变参数的方法首先应该调用不变性检测器来断言传递的实际对象的类的不变性。如果参数的类型是最终类,则可以省略该测试,因此最终性有利于性能,但严格来说不是必需的。另外,正如你将进一步看到的那样,一个类型为非final类的字段将导致声明类被认为是可变的,但仍然是,这是声明类的问题,而不是非final的问题不可变成员类。拥有一个不可变类的高层次结构是完全正确的,其中所有非叶节点当然必须是非final的。

You should not check whether a field is private; it is perfectly fine for a class to have a public field, and the visibility of the field does not affect the immutability of the declaring class in any way, shape, or form. You only need to check whether the field is final and its type is immutable.

你不应该检查一个字段是否是私有的;对于一个拥有公共领域的类来说,这是完全正确的,并且该领域的可见性不会以任何方式,形状或形式影响声明类的不变性。您只需要检查该字段是否为final,其类型是不可变的。

When examining a class, what you want to do first of all is to recurse to determine the immutability of its super class. If the super is mutable, then the descendant is by definition mutable too.

在检查一个类时,首先要做的是递归以确定其超类的不变性。如果super是可变的,那么后代也是可变的。

Then, you only need to check the declared fields of the class, not all fields.

然后,您只需要检查类的声明字段,而不是所有字段。

If a field is non-final, then your class is mutable.

如果一个字段是非final字段,那么你的类是可变的。

If a field is final, but the type of the field is mutable, then your class is mutable. (Arrays are by definition mutable.)

如果一个字段是final,但字段的类型是可变的,那么你的类是可变的。 (根据定义,数组是可变的。)

If a field is final, and the type of the field is Calculating, then ignore it and proceed to the next field. If all fields are either immutable or Calculating, then your class is immutable.

如果字段是final,并且字段的类型是Calculating,则忽略它并继续下一个字段。如果所有字段都是不可变的或计算,那么您的类是不可变的。

If the type of the field is an interface, or an abstract class, or a non-final class, then it is to be considered as mutable, since you have absolutely no control over what the actual implementation may do. This might seem like an insurmountable problem, because it means that wrapping a modifiable collection inside an UnmodifiableCollection will still fail the immutability test, but it is actually fine, and it can be handled with the following workaround.

如果字段的类型是接口,抽象类或非final类,那么它将被视为可变,因为您完全无法控制实际实现可能执行的操作。这似乎是一个不可克服的问题,因为这意味着在UnmodifiableCollection中包装一个可修改的集合仍然会使不变性测试失败,但它实际上很好,并且可以通过以下解决方法来处理它。

Some classes may contain non-final fields and still be effectively immutable. An example of this is the String class. Other classes which fall into this category are classes which contain non-final members purely for performance monitoring purposes (invocation counters, etc.), classes which implement popsicle immutability (look it up), and classes which contain members that are interfaces which are known to not cause any side effects. Also, if a class contains bona fide mutable fields but promises not to take them into account when computing hashCode() and equals(), then the class is of course unsafe when it comes to multi-threading, but it can still be considered as immutable for the purpose of using it as a key in a map. So, all these cases can be handled in one of two ways:

某些类可能包含非final字段,但仍然是有效的不可变字段。一个例子是String类。属于此类别的其他类是包含纯粹用于性能监视目的的非最终成员(调用计数器等)的类,实现冰棒不变性的类(查找它),以及包含已知接口的成员的类不会引起任何副作用。此外,如果一个类包含真正的可变字段但承诺在计算hashCode()和equals()时不考虑它们,那么当涉及到多线程时,该类当然是不安全的,但它仍然可以被认为是为了将它用作地图中的键而不可变。因此,所有这些情况都可以通过以下两种方式之一来处理:

  1. Manually adding classes (and interfaces) to your immutability detector. If you know that a certain class is effectively immutable despite the fact that the immutability test for it fails, you can manually add an entry to your detector which associates it with Immutable. This way, the detector will never attempt to check whether it is immutable, it will always just say 'yes, it is.'

    手动将类(和接口)添加到不变性检测器。如果您知道某个类实际上是不可变的,尽管它的不变性测试失败了,您可以手动向检测器添加一个条目,将其与Immutable相关联。这样,探测器永远不会尝试检查它是否是不可变的,它总是只是说'是,它是'。

  2. Introducing an @ImmutabilityOverride annotation. Your immutability detector can check for the presence of this annotation on a field, and if present, it may treat the field as immutable despite the fact that the field may be non-final or its type may be mutable. The detector may also check for the presence of this annotation on the class, thus treating the class as immutable without even bothering to check its fields.

    介绍@ImmutabilityOverride注释。您的不变性检测器可以检查字段上是否存在此注释,如果存在,它可以将该字段视为不可变字段,尽管字段可能是非最终字段或其类型可能是可变的。检测器还可以检查类上是否存在此注释,从而将类视为不可变,甚至不必检查其字段。

I hope this helps future generations.

我希望这有助于后代。