可以使用==而不是equals来比较Java中的不可变对象

时间:2012-06-10 17:39:30

标签: java equals immutability

考虑两个类型为Integer的引用,它们调用静态工厂方法valueOf,如下所示: -

    Integer a = Integer.valueOf("10"); 
    Integer b = Integer.valueOf("10"); 

考虑到Integer是不可变的,可以使用==而不是使用equals方法来比较a和b。我猜测valueOf方法确保只创建一个值为10的Integer实例,并为每个使用值10创建的Integer返回对此实例的引用。

通常,使用==而不是equals来比较使用相同静态工厂方法调用创建的不可变类的两个引用是否可以?

编辑: Integer类仅用作示例。我知道如果使用==进行比较,那么Intgers高达127将返回true。我需要知道的是,当我创建自己的不可变类时,使用方法create()说MyImmutable将确保不创建重复的MyImmutable对象,如果我比较使用create方法创建的2个MyImmutable引用,那么它是否可以。使用==而不是等于。

5 个答案:

答案 0 :(得分:7)

==和equals()根本不同。

您应该阅读这篇文章了解更多详情:

Difference between Equals/equals and == operator?

它与不可变对象无关。

答案 1 :(得分:6)

不,这一般不安全。 ==运算符会比较引用,而不是值。

使用==恰好适用于-128到127之间的整数,但不适用于其他整数。以下代码演示==并不总是有效:

Integer a = Integer.valueOf(10); 
Integer b = Integer.valueOf(10); 
System.out.println(a == b);

true

Integer c = Integer.valueOf(1000); 
Integer d = Integer.valueOf(1000); 
System.out.println(c == d);

false

查看在线工作:ideone

此行为的解释在于Integer.valueOf

的实施
public static Integer valueOf(int i) {
     final int offset = 128;
     if (i >= -128 && i <= 127) { // must cache
         return IntegerCache.cache[i + offset];
     }
     return new Integer(i);
 }

source

标准还要求小输入(-128到127)的装箱整数为对象提供相同的引用。

  

5.1.7拳击转换

     

如果被装箱的值p为真,假,一个字节,范围为\ u0000到\ u007f的字符,或者介于-128和127之间的整数或短数,则让r1和r2为任意结果p的两次拳击转换。始终是r1 == r2。

的情况

然而,该标准对此范围之外的整数没有这样的保证。


  

通常,使用==而不是equals来比较使用相同静态工厂方法调用创建的不可变类的两个引用是否可以?

如上所示,它一般不起作用。但是如果你确保具有相同值的两个不可变对象总是具有相同的引用,那么是的,它可以工作。但是,您必须仔细遵循一些规则:

  • 构造函数不得公开。
  • 必须缓存通过静态方法创建的每个对象。
  • 每次要求您创建对象时,必须首先检查缓存以查看是否已创建具有相同值的对象。

答案 2 :(得分:5)

如果您的工厂方法为相同的输入返回相同的对象,则可以安全地将它们与==进行比较。例如,String.intern以这种方式工作。枚举也可以与==进行比较。但是Integer.valueOf仅为-128 ... 127范围返回相同的对象(默认配置)。

Integer.valueOf(127) == Integer.valueOf(127)

Integer.valueOf(128) != Integer.valueOf(128)

一般来说,您应该使用equals方法来比较任何对象。当对象具有少量不同值时,可以使用运算符==来提高性能。除非你100%确定自己在做什么,否则我不建议使用这种方法。

答案 3 :(得分:2)

不可变性和平等并不一定与彼此有关。 ==比较引用相等性,这意味着,它比较两个变量是否指向对象的同一个实例。平等意味着两个对象共享相同的值。现在,不变性意味着你不能在构造之后改变它。

所以,你可能有两个不可变的obejcts,表示相同的值(意思是,它们是等于a.equals(b)返回true)但是它们不是同一个实例。

我在这里有一个小例子:

public class MyPoint {
    private int x;
    private int y;

    public MyPoint(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof MyPoint))
            return false;
        MyPoint p = (MyPoint) obj;
        return this.x == p.x && this.y == p.y;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        MyPoint p = new MyPoint(2, 2);
        MyPoint q = new MyPoint(2, 2);
        MyPoint r = q;

        System.out.println(p == q);
        System.out.println(p == r);
        System.out.println(q == r);
        System.out.println(p.equals(q));
        System.out.println(p.equals(r));
        System.out.println(q.equals(r));

    }

}

输出是: 假 假 真正 真正 真正 真

MyPoint 不可变。在初始化之后,您无法更改其值/状态。但是,正如您所看到的,myPoint的两个对象可能相等,但它们可能不是同一个实例。

我认为你所想到的是某种flyweight模式,其中对象的每个可能状态只存在一个对象。 Flyweight也通常意味着那些obejcts是不可变的。

答案 4 :(得分:1)

它们不是同一个对象,因此==不会是真的。使用对象时,请确保安全并使用equals()