`
onray
  • 浏览: 6210 次
  • 来自: ...
最近访客 更多访客>>
社区版块
存档分类
最新评论

Mutable and Immutable Objects

    博客分类:
  • java
阅读更多

Mutable and Immutable Objects
by David O'Meara

Mutable and Immutable objects are a simple idea with wide ranging consequences. Often when immutability is mentioned it isn't defined, or when it is defined there are no details of how to ensure immutability or the consequences.

Before we start, the terminology follows the article Pass-by-Value, Please in the JavaRanch Camp fire. If you haven't learnt to say Java is pass-by-value, you might want to head there first.


Crappy Definition to start off with:
Mutable Objects: When you have a reference to an instance of an object, the contents of that instance can be altered
Immutable Objects: When you have a reference to an instance of an object, the contents of that instance cannot be altered


Immutability and Instances
To demonstrate this behaviour, we'll use java.lang.String as the immutable class and java.awt.Point as the mutable class. Point myPoint = new Point( 0, 0 );
System.out.println( myPoint );
myPoint.setLocation( 1.0, 0.0 );
System.out.println( myPoint );

String myString = new String( "old String" );
System.out.println( myString );
myString.replaceAll( "old", "new" );
System.out.println( myString );

In case you can't see what the output is, here it is:
java.awt.Point[0.0, 0.0]
java.awt.Point[1.0, 0.0]
old String
old String
We are only looking at a single instance of each object, but we can see that the contents of myPoint has changed, but the contents of myString did not. To show what happens when we try to change the value of myString, we'll extend the previous example. String myString = new String( "old String" );
System.out.println( myString );
myString = new String( "new String" );
System.out.println( myString );

The output from this is:
old String
new String
Now we find that the value displayed by the myString variable has changed. We have defined immutable objects as being unable to change in value, so what is happening? Let's extend the example again to watch the myString variable closer. String myString = new String( "old String" );
String myCache = myString;
System.out.println( "equal: " + myString.equals( myCache ) );
System.out.println( "same:  " + ( myString == myCache ) );

myString = "not " + myString;
System.out.println( "equal: " + myString.equals( myCache ) );
System.out.println( "same:  " + ( myString == myCache ) );

The result from executing this is:
equal: true
same:  true
equal: false
same:  false
What this shows is that variable myString is referencing a new instance of the String class. The contents of the object didn't change; we discarded the instance and changed our reference to a new one with new contents.


Variable Values and Instance Contents
If you look at the example above, you can see the point I'm trying to sneak through. You can always change the value of a variable by getting your variable to reference a new object. Sometimes you can change the value of a variable by keeping a reference to the same instance, but change the contents of the instance.

After you have eliminated those possibilities, you have a variable that retains its reference to an object, but the contents of this object cannot change. Doesn't sound like a very interesting idea, and it sounds a bit too simple to be useful.

It turns out that Immutable Objects, that is objects that you cannot change the contents after they have been set, are a very handy tool when used in the right place. They can promote thread safety in your code, you can share them around without being afraid that they will change without your knowledge, they are great for caching and constants. But we're not going to cover any of that yet; we are going to concentrate on building immutable objects.


Building an Immutable class
So what is it about the String class that makes it Immutable while a Point is mutable?

In this case, Strings have no mutators while Points do. If we removed all of the mutators from the Point class, would it be Immutable? No it wouldn't. Removing mutators is a necessary first step, but immutability requires more than that to ensure that the contents of an instance never changes.


Fields must be private
Obviously all of the fields must be private. There is little point removing the mutators if they aren't even required to change the instance contents. public class ImmutablePoint
{
//note there are no mutators!
private double x;
private double y;

//and the rest...


This is almost enough, but there are two more steps to consider.


Make sure methods can't be overridden.
If your class gets extended, it could add extra fields that are not immutable, or the methods could be overridden to return a different value each time. There are two ways to protect against this.

The preferred way is to make the class final. This is sometimes referred to as "Strong Immutability". It prevents anyone from extending your class and accidentally or deliberately making it mutable.

The second way, also called "Weak Immutability" is to make your methods final. It allows others to extend your class to add more behaviour, but protects the original contract specified by the class. If you want a more verbose description, imagine a class A is weakly immutable. If you have an instance of object A, it is immutable. If someone creates class B that extends A, it is only the behaviour defined by the A class that is immutable. Any added behaviour from class B may not be immutable.


Protect mutable fields
The last requirement which many people fall victim too, is to build your immutable class from primitive types or immutable fields, otherwise you have to protect mutable fields from manipulation.

To highlight this problem, we'll use the example of a supposedly immutable class representing a person. Our class has a first and last name, as well as a date of birth. import java.util.Date;
public final class BrokenPerson
{
private String firstName;
private String lastName;
private Date dob;

public BrokenPerson( String firstName,
  String lastName, Date dob)
{
this.firstName = firstName;
this.lastName = lastName;
this.dob = dob;
}

public String getFirstName()
{
return this.firstName;
}
public String getLastName()
{
return this.lastName;
}
public Date getDOB()
{
return this.dob;
}
}

This all looks fine, until someone uses it like this: Date myDate = new Date();
BrokenPerson myPerson =
  new BrokenPerson( "David", "O'Meara", myDate );
System.out.println( myPerson.getDOB() );
myDate.setMonth( myDate.getMonth() + 1 );
System.out.println( myPerson.getDOB() );

Depending on the dates entered, the output could be something like this:

Mon Mar 24 21:34:16 GMT+08:00 2003
Thu Apr 24 21:34:16 GMT+08:00 2003
The Date object is mutable, and the myPerson variable is referencing the same instance of the Date object as the myDate variable. When myDate changes the instance it is referencing, the myPerson instance changes too. It isn't immutable!

We can defend against this by taking a copy of the of the Date instance when it is passed in rather than trusting the reference to the instance we are given. import java.util.Date;
public final class BetterPerson
{
private String firstName;
private String lastName;
private Date dob;

public BetterPerson( String firstName,
  String lastName, Date dob)
{
this.firstName = firstName;
this.lastName = lastName;
this.dob = new Date( dob.getTime() );
}
//etc...

Now we're close, but we're still not quite there. Our class is still open to abuse. BetterPerson myPerson =
  new BetterPerson( "David", "O'Meara", new Date() );
System.out.println( myPerson.getDOB() );
Date myDate = myPerson.getDOB();
myDate.setMonth( myDate.getMonth() + 1 );
System.out.println( myPerson.getDOB() );

We see here that taking a copy on the way in wasn't enough; we also need to prevent anyone from getting a reference to our mutable Date field when we pass it out. public Date getDOB()
{
return new Date( this.dob.getTime() );
}




Make deep copies of mutable data
The only point to add is that when you copy the instance on the way in and the way out, you need to make a deep copy. Otherwise you run the risk of leaving some mutable data in your immutable class!

If you are confused about the need to provide a deep copy, keep in mind that a single piece of shared mutable data, no matter how deep it is buried inside an object, makes your class mutable. When you create a copy of an object to defend against the value changing, you need to make sure your copy doesn't include this shared mutable class. You need to copy any mutable objects all the way down to the last field, and copy any nested fields until you have a completely new copy of your own. It's the only way to be safe!


Our Template for Immutable Classes
Now we have a template for creating immutable objects.
Make all fields private
Don't provide mutators
Ensure that methods can't be overridden by either making the class final (Strong Immutability) or making your methods final (Weak Immutability)
If a field isn't primitive or immutable, make a deep clone on the way in and the way out.


Which classes are Immutable?
To finish up, lets discuss the common Java classes that are immutable and those that aren't. Firstly, all of the java.lang package wrapper classes are immutable: Boolean, Byte, Character, Double, Float, Integer, Long, Short, String.

As in the Person classes we discussed, java.util.Date objects are not immutable. The classes java.math.BigInteger and BigDecimal are not immutable either, although maybe they should have been.


And we're done...
...for now. This concludes an introduction to Mutable and Immutable Classes in Java. Hopefully there will be a second part that will go into more detail on weak and strong immutability, reasons why you'd make classes immutable and reasons to avoid them, and some other miscellaneous topics on immutable types in Java.

分享到:
评论

相关推荐

    Get Programming - Learn to code with Python.epub

    Lesson 24 - Mutable and immutable objects Lesson 25 - Working with lists Lesson 26 - Advanced operations with lists Lesson 27 - Dictionaries as maps between objects Lesson 28 - Aliasing and copying ...

    Collections

    Arrays can be mutable or immutable. Immutable arrays do not allow modification once created, while mutable arrays can be modified by adding, removing, or replacing elements. Arrays are initialized ...

    Java 9 with JShell

    Mutable and Immutable Classes Inheritance, Abstraction, Extension, and Specialization Members Inheritance and Polymorphism Contract Programming with Interfaces Advanced Contract Programming with ...

    effective-java 配套代码

    4. **可变与不可变对象(Mutable vs Immutable Objects)**: 通过示例代码展示了如何创建不可变对象,以及不可变对象的益处和实现策略。 5. **泛型(Generics)**: 书中深入讲解了Java泛型的用法,包括类型擦除、...

    java 第四版英文.pdf.zip

    8. **可变与不可变对象(Mutable vs Immutable Objects)**:书中探讨了如何创建和使用不可变对象,以及它们在并发编程中的优势。 9. **多线程编程(Multithreaded Programming)**:Java提供了丰富的并发工具,如`...

    python3.6.5参考手册 chm

    PEP 471 - os.scandir() function – a better and faster directory iterator PEP 475: Retry system calls failing with EINTR PEP 479: Change StopIteration handling inside generators PEP 485: A function...

    java英文笔试

    Immutable objects are useful for ensuring thread safety and simplifying concurrent programming. #### 14. How to write a program using a sort program. **Example Answer**: To write a program that ...

    INTRODUCTION TO PYTHON SCRIPTING FOR MAYA ARTISTS.

    They are mutable and can contain elements of different types. **Tuples** Tuples are similar to lists but are immutable, meaning they cannot be modified after creation. **Dictionaries** Dictionaries...

    iOS Fuondation Framework Reference

    - `+ arrayWithObjects:`: Creates and returns a new array containing the specified objects. - `+ arrayWithArray:`: Creates and returns a new array initialized with the contents of another array. **...

    Kotlin - Kotlin Language Documentation 2.0.0

    - **Lists:** Immutable (`List`) and mutable (`MutableList`) - **Sets:** Immutable (`Set`) and mutable (`MutableSet`) - **Maps:** Immutable (`Map`) and mutable (`MutableMap`) For example, to create a ...

    javascript权威指南(第六版)

    3.7 Immutable Primitive Values and Mutable Object References 44 3.8 Type Conversions 45 3.9 Variable Declaration 52 3.10 Variable Scope 53 4. Expressions and Operators . . . . . . . . . . . . . . . . ...

    Python基础——笔试面试利器

    - **不可更改对象**(Immutable Objects): 在Python中,字符串(string)、元组(tuple)和数字(number)等都是不可更改的对象。这意味着一旦创建这些类型的对象,就不能改变其值。 - **可更改对象**(Mutable ...

    Python 2.4 Quick Reference Card (Letter) (2007).pdf

    Mutable/Immutable Objects..................2 Namespaces.........................................2 Constants, Enumerations......................2 Flow Control........................................2 ...

    Practical Java(中文版(繁体+简体))

    實踐64:欲傳遞或接收mutable objects(可變對象)之object references 時,請實施clone() 215 實踐65:使用繼承(inheritance)或委託(delegation)來定義 immutable classes(不可變類) 226 目錄 Practical Java...

    12.PythonicOOP上1

    Python的数据模型基于Objects,这意味着程序中的数据、代码甚至元数据都是以对象的形式存在。每个对象都有三个关键组成部分:身份(Identity)、类型(Type)和值(Value)。身份是对象独一无二的标识,由`id()`函数...

    在python中list作函数形参,防止被实参修改的实现方法

    在Python中,对象分为两类:**可变对象(mutable objects)** 和 **不可变对象(immutable objects)**。其中,列表(list)属于可变对象,而整数(int)、字符串(string)等则属于不可变对象。 - **不可变对象**:当作为...

    Effective Objective-C 2.0

    集合类遵循了不可变(immutable)和可变(mutable)的区分。 9. GCD和NSOperation:GCD(Grand Central Dispatch)和NSOperation是Objective-C中用于处理多线程和并发的高级抽象。GCD提供了一种简单的API来描述任务...

    chronos:最初基于Carbon的独立DateTime库

    CakePHP Chronos Chronos旨在取代nesbot/carbon 。 它着重于提供不变的日期/日期时间对象。 不可变的对象有助于确保不会意外修改日期时间对象,从而使数据更可预测。安装使用composer安装: $ composer require ...

    JDO使用手册

    Java Data Objects (JDO) 是一种用于管理和操作持久化对象的标准API。它提供了一种简单而强大的方式来处理对象到数据库之间的映射,适用于各种数据库环境。JDO的目标是为开发人员提供一个统一的数据访问接口,使得...

Global site tag (gtag.js) - Google Analytics