2014-11-01 59 views
1

是否需要使不可变类private的字段好像它们被标记为final一样,不能更改?
我的意思是仅仅将这些字段标记为final就足够了吗?java中的不可变类

(我知道这是没有必要的不可变类有最终的领域,但它最好有它的类编译时检查内部。)

+0

成员字段可能有它们自己的mutator方法,所以你不希望这些方法被公开。 – 2014-11-01 10:27:50

回答

1

让私有变量通过getter访问给你更多的自由与类的实现。例如,您可以用一个计算或检索访问值或缓存结果的实现来替换一个简单的getter。

将字段保持为私有状态的另一个原因是即使当字段是可变对象时也是如此。在这种情况下,对象可以改变,即使它是最终的,通常不是你想要的。这是一个大概是不可变的类的例子,它带有一个可变的Date类的公共final字段。

class DateHolder { 
     public final Date date; 
     DateHolder(Date date) { 
      this.date = date: 
     } 
} 

// ... 

DateHolder holder = new DateHolder(Date.now()); 

// this doesn't work because date is final: 
//holder.date = new Date(2013, 11, 23); 

// but this works even though date is final: 
holder.date.setYear(2013); 
+0

我明白了。结合这个答案和鲍里斯蜘蛛的答案使我对它更加清楚。保持字段私有和getters返回不可变。谢谢 ! – iAmLearning 2014-11-01 11:05:01

1

final场只能从构造方法中分配给。因此,制作字段final足以使它们在建造后不可变。分配指向可变对象的指针是另一回事。

+1

这个不同的故事会是更有趣的一个。 – Philipp 2014-11-01 10:36:45

+0

'final'字段也可以在声明时初始化。我也同意Philipp的观点。抛开“有趣”的部分,这是一个无法回答的问题。 – 2014-11-01 10:37:11

7

没有这是不够的。

考虑这个例子:

final class ImmutableClass { 
    private final List<String> data; 

    public ImmutableClass(final List<String> data) { 
     this.data = data; 
    } 

    public List<String> getData() { 
     return data; 
    } 
} 

这个类是final所以不能延长。这是data字段是final因此在分配后不能更改。

final ImmutableClass immutableClass = new ImmutableClass(data); 
immutableClass.getData().add("Some other value"); 

哎呀。

因此,为了使一类真正不变的各个领域也应该是一成不变的类,或有防守副本

例如,纠正问题,你需要做的是:

final class ImmutableClass { 
    private final List<String> data; 

    public ImmutableClass(final List<String> data) { 
     this.data = new ArrayList<>(data); 
    } 

    public List<String> getData() { 
     return Collections.unmodifiableList(data); 
    } 
} 

而这只是因为作品String是不可改变的。如果您有,例如,List<Date>您还需要复制个人Date对象。

+0

感谢您宝贵的答复。 – iAmLearning 2014-11-01 11:08:05

+0

我想用refelction API可以在运行时更改数据字段的内容吗? – 2015-01-07 17:32:38

+0

@KickButtowski用反射可以将'Boolean.TRUE'的值更改为'false'。防止反射的唯一方法是使用“SecurityManager”。因此,在大多数情况下,反射的影响被忽略。 – 2015-01-07 17:34:38