如何识别 Java 中的不可变对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/203475/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I identify immutable objects in Java
提问by mcherm
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 Stringor 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.
编辑添加:感谢有见地和有用的答案。正如一些答案指出的那样,我忽略了定义我的安全目标。这里的威胁是无知的开发人员——这是一段框架代码,将被大量对线程几乎一无所知并且不会阅读文档的人使用。我不需要防御恶意开发人员——任何足够聪明来改变字符串或执行其他恶作剧的人也会足够聪明地知道在这种情况下它是不安全的。代码库的静态分析是一种选择,只要它是自动化的,但不能指望代码,因为不能保证每个都会有精通线程的者。
采纳答案by Simon Lehmann
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.
更新:正如评论中所建议的,它可以扩展为在超类上递归,而不是检查某个类。还建议在 isValidFieldType 方法中递归使用 isImmutable。这可能会奏效,我也做了一些测试。但这并非微不足道。您不能只通过调用 isImmutable 来检查所有字段类型,因为 String 已经无法通过此测试(它的字段hash
不是最终的!)。此外,您很容易遇到无休止的递归,从而导致StackOverflowErrors;) 其他问题可能是由泛型引起的,您还必须检查它们的类型是否具有不变性。
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).
我认为通过一些工作,这些潜在的问题可能会以某种方式解决。但是,你必须先问问自己这是否真的值得(也是性能方面的)。
回答by SCdF
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 mustbe 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 makestheir class immutable.
编辑:其他人建议使用不可变的注释。这很好,但您也需要文档。否则人们只会想“如果我把这个注释放在我的类上,我可以将它存储在集合中”,然后就会把它放在任何东西上,不可变的和可变的类都一样。事实上,我会谨慎使用不可变的注释,以防人们认为注释使他们的类不可变。
回答by Jason Cohen
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.
当然,您仍然依赖于作者信守其不变性的承诺,但由于作者明确添加了注释,因此这是一个合理的假设。
回答by Pablo Fernandez
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)
然后通过在类上使用反射,检查它是否有注释(我会粘贴代码但它的样板文件,可以很容易地在网上找到)
回答by OscarRyz
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,
也不能覆盖任何方法,所有字段都是最终的和私有的,
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”一书的副本,它有一个与此主题相关的项目。虽然 is 并不确定如何检测不可变类,但它展示了如何创建一个好的类。
The item is called: "Favor immutability"
该项目称为:“支持不变性”
回答by Daniel Hiller
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.
我只会在附加时引入一个接口“不可变”来检查。这表明无论出于何种原因,都应该只插入不可变对象。
interface Immutable {}
class MyImmutable implements Immutable{...}
public void add(Object o) {
if (!(o instanceof Immutable) && !checkIsImmutableBasePrimitive(o))
throw new IllegalArgumentException("o is not immutable!");
...
}
回答by Martin Probst
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...
您可以使用不允许循环的“可见”映射来解决该问题,或者使用一些非常聪明的代码来确定类是不可变的,如果它们的所有依赖项仅依赖于它们自己是不可变的,但这将非常复杂......
回答by Benno Richters
Use the Immutableannotation from Java Concurrency in Practice. The tool FindBugscan then help in detecting classes which are mutable but shouldn't be.
在实践中使用Java Concurrency 中的Immutable注释。然后,FindBugs工具可以帮助检测可变但不应该可变的类。
回答by james
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.
为什么所有的建议都要求课程是最终的?如果您使用反射来检查每个对象的类,并且您可以通过编程方式确定该类是不可变的(不可变的,最终字段),那么您不需要要求类本身是最终的。