当预期 Immutable 对象时,可变子类对象如何导致问题的代码示例

Code Example for how a Mutable subclass object can cause issue when Immutable object is expected

提问人:maddy man 提问时间:6/26/2016 最后编辑:Communitymaddy man 更新时间:6/26/2016 访问量:105

问:

我一直在问这个问题:为什么人们会在 Java 中声明一个不可变的类 final?

我理解这个答案,但需要一个代码示例。

我写了它,但有一些疑问,如果有人能帮忙,将不胜感激。

    public class Immutable {
            private final int value;

            public Immutable(int value) {
                this.value = value;
            }

            public int getValue() {
                return value;
            }

            public boolean equals(Object obj) { 
                return (obj instanceof Immutable 
                          && getValue() == ((Immutable) obj).getValue());
                }

            public int hashCode() { 
                  int hash = 1;
                  hash = hash * 31 + value;
                  return hash;
                }
        }

        public class Mutable extends Immutable {
            private int realValue;

            public Mutable(int value) {
                super(value);

                realValue = value;
            }

            public int getValue() {
                return realValue;
            }
            public void setValue(int newValue) {
                realValue = newValue;
            }
        }

// test class main()
   Mutable key = new Mutable(30);
   Hashtable<Immutable, String> ht = new Hashtable<Immutable,String>();

   ht.put(new Immutable(10), "10");
   ht.put(new Immutable(20), "20");
   ht.put(key, "30");

   System.out.println("Hashcode : "+key.hashCode()+", \tKey : "+key.getValue()+" => Value : "+ht.get(key));

   key.setValue(40);
   System.out.println("Hashcode : "+key.hashCode()+", \tKey : "+key.getValue()+" => Value : "+ht.get(key));

Output : 
Hashcode : 61,  Key : 30 => Value : 30
Hashcode : 61,  Key : 40 => Value : 30

我无法将给出的答案与此代码联系起来。

爪哇岛 字符串 不变性 最后 可变

评论


答:

0赞 dpr 6/26/2016 #1

您的代码正在正常工作,因为您的方法直接使用类中的最终字段,而不是使用字段的 getter。使用 from 方法时,此字段不会更改,因为此方法仅适用于该字段。hashCodevalueImmutablesetValueMutablerealValue

如果更改为hashCode

public int hashCode() { 
    int hash = 1;
    hash = hash * 31 + getValue(); // use getter here
    return hash;
}

您将观察到此答案中描述的不良行为。

需要注意的是,您的代码实际上违反了等价合约。由于两个实例可能彼此相等,但具有不同的哈希码......Mutable

评论

0赞 maddy man 6/26/2016
我将equals方法更新为:public boolean equals(Object obj) { return (obj instanceof Immutable && obj.hashCode() == this.hashCode() && getValue() == ((Immutable) obj).getValue()); }
0赞 dpr 6/27/2016
这将履行等价合同。但是,这并不是真正的最佳实践。考虑阅读此问题/答案,了解如何实现正确的等于和哈希码方法。