在我的代码,我在创建一个收藏的物体将通过各种线在一个时尚,仅仅是安全的,如果对象是不可变的。当试图插入一个新的对象到我的收藏,我想测试,看它是否是不可改变的(如果不是,我会扔一个例外)。

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

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

是否有某种方式(也许使用反射),我可以可靠地探测是否一类是不可改变的?误报(认为它是不可改变的时候它不是)是不能接受的,但假阴性(认为它是可变的时候它不是)。

编辑,以增加: 谢谢你的富有见解和有益的答案。一些答复指出,我被忽视的定义我的安全目标。的威胁,这是无能开发人员--这是一块框架码,将使用大量的人谁知道下一个-来-什么线和不可读的文件。我不需要抵御恶意开发人员--任何人都聪明 变异字符串 或执行其他的诡计也将足够的智慧,知道这不是安全在这种情况。静态分析代码是一个选项,只要它是自动的,但码的评论,不可以算的上是因为没有保证每一审查将有穿精明的评审。

有帮助吗?

解决方案

有没有可靠的方式来检测,如果一类是不可改变的。这是因为有这么多方式的一个属性的一类可能会改变而你不能检测到所有的它们通过反射。

只有这样,才能获得接近于这是:

  • 只允许最终性质的类型是不可改变的(基本类型和类的你知道是固定不变的),
  • 需要类最后的自己
  • 要求他们继承一个基类提供(这是保证是不可改变的)

然后你可以检查与下列代码,如果对象是不可变的:

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

更新: 作为建议的意见,它可以扩大到recurse在超类而不是检查对于某些类。还有人建议使用递归isImmutable在isValidFieldType方法。这也许可以工作,我也做了一些测试。但是,这不是微不足道的。你不能只是检查所有领域的类型呼吁isImmutable,因为串已经失败,这种试验(其领域 hash 是不是最终的!).你也很容易进行无休止的递归,造成 StackOverflowErrors ;)其他问题可能造成的仿制药,在那里你也要检查他们的类型immutablity.

我认为有一些工作,这些潜在的问题可能会得到解决。但是然后,你必须先问问你自己,如果这真是值得(也表现的明智的).

其他提示

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

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

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

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

基本上没有。

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

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

这可能是另一个提示:

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

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

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

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

该项目被称为:“赞成不可变性”

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

  

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

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

假设你有这个课程:

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

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

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

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

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

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

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

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

你可以用一个不允许循环的“看到”地图来修复它,或者用一些非常聪明的代码来决定类是不可变的,如果它们的所有依赖都是不可变的,只取决于它们自己,但那将会非常复杂...... / p>

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

元数据:

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

客户代码:

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

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

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

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

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

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

我只想介绍一个界面“不可变”。附加时检查。这可以作为一个暗示,只有在你做这件事的时候才应该插入不可变对象。

interface Immutable {}

class MyImmutable implements Immutable{...}

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

试试这个:

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

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

您可以在以下网址查看: www.mutabilitydetector.org

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

/*
* 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!
    */
}

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

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

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

我赞赏和欣赏的工作量Grundlefleck已投入他的可变性检测器,但我认为这是一个有点矫枉过正。你可以写一个简单但实际上非常充足的(即, 务实的)探测器如下:

(注:这是一个副本,我在这里评论: https://stackoverflow.com/a/28111150/773113)

首先,你不会只是写一个方法,该方法确定是否一类是不可改变;相反,你会需要编写一个永久性检测器课,因为它将不得不保持一些国家。状态检测器,将检测到的不可改变的各类其它已审查了这么远。这不仅是有用性,但它实际上是必要的,因为一类可以包含一个圆形的参考,这会导致一种简单化的不变性检测器落入无限的递归。

这不可变性的一类有四个可能值: Unknown, Mutable, Immutable, , Calculating.你可能会想要有一个地图相关联的各类已经遇到这么远的一个不可改变的价值。当然, Unknown 实际上并不需要实施,因为这将是暗示的国家的任何一类没有在图。

所以,当你开始审查一类中,可将其与一个 Calculating 值的地图,并且当你完成后,你替换 Calculating 不管是 ImmutableMutable.

对于每一类,只需要检查的场成员,不代码。这个想法的检查的字节是相当错误的。

首先,你应该 检查是否一类的最后;的终局的一类不会影响它的不变性。相反,一种方法,它希望一个不变的参数应该首先援引不可变性检测器的维护的不变性之类的实际目的,是通过。这个测试如果可以省略的类型参数是最后一类,所以最终是良好的业绩,但是严格来说没有必要的。还有,你会看到进一步下,一个领域,其类型为一个非终类会引起这类声明应被认为是可变的,但是仍然,这是一个问题的声明类,而不是问题的非最终的不可改变的会员类。它是完全没有一个高层次结构的一成不变的类,其所有非叶节点当然必须是非最终的。

你应该 检查是否一个领域是私有的;这是完美的一类有一个公共领域,并且可见的领域并不影响的不可改变的声明类在任何方式、形态或形式。你只需要检查是否场是最终和其类型是不可改变的。

在审查时一个班,你想要做什么首先是recurse确定不可改变它的 super 类。如果超级是可变的,随后的后裔是通过可变定义。

然后,你只需要检查 宣布 领域之类的,不是 所有 领域。

如果一个领域是非终,你的类是可变的。

如果一个领域是最终的,但类型的领域是可变的,那么你的课就是可变的。(阵列是通过定义可变的。)

如果一个领域是最终的、类型和领域是 Calculating, 然后忽视它,并继续到下一个领域。如果所有领域都是不可改变的,或者 Calculating, 然后你的类是不可改变的。

如果该类型的领域是一个接口,或者一个抽象的类或非最终类,那么它被认为是可变的,因为你绝对不能控制什么实际执行情况可以这样做。这可能看起来像一个不可逾越的问题,因为它意味着包裹的可修改的收集内 UnmodifiableCollection 仍然会失败的不可变性的试验,但它实际上是好的,它可以处理与以下解决方法。

某些类别可以包含非最后的领域以及仍然能 有效的不可改变的.这样的一个例子是的 String 类。其他类别归入这个类别的类别含有非最终的成员纯粹为表现监测的目的(调用柜台等等), 类其实施 冰棒不可变性 (看起来),以及课程含有成员的接口,这是众所周知,不会造成任何副作用。此外,如果一类包含善意变的领域,但承诺不把他们考虑到在计算哈希码()以及平等(),然后类的当然是不安全的时候多线程,但它仍然可以被认为是不可变的目的使用它作为一个关键的在线图。因此,所有这些情况下可以处理两种方式之一:

  1. 手动中添加类(和接口)给你的永久性检测器。如果你知道某一类是有效的不可改变的,尽管事实上的永久性测试失败的话,你可以手动中添加一个条目来你的检测器,将它与 Immutable.这样,探测器将永远不会尝试,以检查它是否是永恒不变的,它将永远只是说'是的,它是。'

  2. 介绍一个 @ImmutabilityOverride 注释。你不可变性的检测器可以检查是否存在这个注解在一个领域,并且如果存在,它可能治疗领域作为不可改变的,尽管事实上,该领域可能非最后的,或其类型可以是可变的。探测器还可以检查是否存在这个注解在类,因而治疗的类为不可改变的,甚至没有打扰到检查它的领域。

我希望这有助于未来世代。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top