📄 tij0129.html
字号:
<html><body>
<table width="100%"><tr>
<td>
<a href="http://www.bruceeckel.com/javabook.html">Bruce Eckel's Thinking in Java</a>
</td>
<td align="right">
<a href="tij_c.html">Contents</a> | <a href="tij0128.html">Prev</a> | <a href="tij0130.html">Next</a>
</td>
</tr></table>
<hr>
<H2 ALIGN=LEFT>
Read-only
classes
<P><A NAME="Index1489"></A></H2>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">While
the local copy produced by
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>clone( )
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">gives
the desired results in the appropriate cases, it is an example of forcing the
programmer (the author of the method) to be responsible for preventing the ill
effects of aliasing. What if you’re making a library that’s so
general purpose and commonly used that you cannot make the assumption that it
will always be cloned in the proper places? Or more likely, what if you
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>want</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
to allow aliasing for efficiency – to prevent the needless duplication of
objects – but you don’t want the negative side effects of aliasing?
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">One
solution is to create <A NAME="Index1490"></A><A NAME="Index1491"></A></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>immutable
objects
</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
which belong to read-only classes. You can define a class such that no methods
in the class cause changes to the internal state of the object. In such a
class, aliasing has no impact since you can read only the internal state, so if
many pieces of code are reading the same object there’s no problem.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">As
a simple example of immutable objects, Java’s standard library contains <A NAME="Index1492"></A><A NAME="Index1493"></A>“wrapper”
classes for all the primitive types. You might have already discovered that, if
you want to store an
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>int</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
inside a collection such as a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Vector</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
(which takes only
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Object</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
handles), you can wrap your
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>int</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
inside the standard library
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Integer</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
class:
</FONT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: ImmutableInteger.java</font>
<font color="#009900">// The Integer class cannot be changed</font>
<font color="#0000ff">import</font> java.util.*;
<font color="#0000ff">public</font> <font color="#0000ff">class</font> ImmutableInteger {
<font color="#0000ff">public</font> <font color="#0000ff">static</font> <font color="#0000ff">void</font> main(String[] args) {
Vector v = <font color="#0000ff">new</font> Vector();
<font color="#0000ff">for</font>(<font color="#0000ff">int</font> i = 0; i < 10; i++)
v.addElement(<font color="#0000ff">new</font> Integer(i));
<font color="#009900">// But how do you change the int</font>
<font color="#009900">// inside the Integer?</font>
}
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Integer</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
class (as well as all the primitive “wrapper” classes) implements
immutability in a simple fashion: they have no methods that allow you to change
the object.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">If
you do need an object that holds a primitive type that can be modified, you
must create it yourself. Fortunately, this is trivial:
</FONT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: MutableInteger.java</font>
<font color="#009900">// A changeable wrapper class</font>
<font color="#0000ff">import</font> java.util.*;
<font color="#0000ff">class</font> IntValue {
<font color="#0000ff">int</font> n;
IntValue(<font color="#0000ff">int</font> x) { n = x; }
<font color="#0000ff">public</font> String toString() {
<font color="#0000ff">return</font> Integer.toString(n);
}
}
<font color="#0000ff">public</font> <font color="#0000ff">class</font> MutableInteger {
<font color="#0000ff">public</font> <font color="#0000ff">static</font> <font color="#0000ff">void</font> main(String[] args) {
Vector v = <font color="#0000ff">new</font> Vector();
<font color="#0000ff">for</font>(<font color="#0000ff">int</font> i = 0; i < 10; i++)
v.addElement(<font color="#0000ff">new</font> IntValue(i));
System.out.println(v);
<font color="#0000ff">for</font>(<font color="#0000ff">int</font> i = 0; i < v.size(); i++)
((IntValue)v.elementAt(i)).n++;
System.out.println(v);
}
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Note
that
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>n</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
is friendly to simplify coding.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>IntValue</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
can be even simpler if the default initialization to zero is adequate (then you
don’t need the constructor) and you don’t care about printing it
out (then you don’t need the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>toString( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">):</FONT><P></DIV><DIV ALIGN=LEFT><TT><FONT FACE="Courier New" SIZE=3 COLOR="Black">class
IntValue { int n; }
</FONT></TT><P></DIV><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Fetching
the element out and casting it is a bit awkward, but that’s a feature of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Vector,</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
not of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>IntValue</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.</FONT><a name="_Toc408018670"></a><P></DIV>
<A NAME="Heading384"></A><H3 ALIGN=LEFT>
Creating
read-only classes
</H3>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">It’s
possible to create your own read-only class. Here’s an example:
</FONT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: Immutable1.java</font>
<font color="#009900">// Objects that cannot be modified</font>
<font color="#009900">// are immune to aliasing.</font>
<font color="#0000ff">public</font> <font color="#0000ff">class</font> Immutable1 {
<font color="#0000ff">private</font> <font color="#0000ff">int</font> data;
<font color="#0000ff">public</font> Immutable1(<font color="#0000ff">int</font> initVal) {
data = initVal;
}
<font color="#0000ff">public</font> <font color="#0000ff">int</font> read() { <font color="#0000ff">return</font> data; }
<font color="#0000ff">public</font> <font color="#0000ff">boolean</font> nonzero() { <font color="#0000ff">return</font> data != 0; }
<font color="#0000ff">public</font> Immutable1 quadruple() {
<font color="#0000ff">return</font> <font color="#0000ff">new</font> Immutable1(data * 4);
}
<font color="#0000ff">static</font> <font color="#0000ff">void</font> f(Immutable1 i1) {
Immutable1 quad = i1.quadruple();
System.out.println("i1 = " + i1.read());
System.out.println("quad = " + quad.read());
}
<font color="#0000ff">public</font> <font color="#0000ff">static</font> <font color="#0000ff">void</font> main(String[] args) {
Immutable1 x = <font color="#0000ff">new</font> Immutable1(47);
System.out.println("x = " + x.read());
f(x);
System.out.println("x = " + x.read());
}
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">All
data is
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>private</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
and you’ll see that none of the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>public</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
methods modify that data. Indeed, the method that does appear to modify an
object is
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>quadruple( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
but this creates a new
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Immutable1</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
object and leaves the original one untouched.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
method
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>f( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
takes an
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Immutable1</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
object and performs various operations on it, and the output of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>main( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
demonstrates that there is no change to
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>x</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
Thus,
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>x</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">’s
object could be aliased many times without harm because the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Immutable1</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
class is designed to guarantee that objects cannot be changed.
</FONT><a name="_Toc408018671"></a><P></DIV>
<A NAME="Heading385"></A><H3 ALIGN=LEFT>
The
drawback to immutability
</H3>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Creating
an immutable class seems at first to provide an elegant solution. However,
whenever you do need a modified object of that new type you must suffer the
overhead of a new object creation, as well as potentially causing more frequent
garbage collections. For some classes this is not a problem, but for others
(such as the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>String</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
class) it is prohibitively expensive.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
solution is to create a companion class that
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>can</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
be modified. Then when you’re doing a lot of modifications, you can
switch to using the modifiable companion class and switch back to the immutable
class when you’re done.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
example above can be modified to show this:
</FONT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: Immutable2.java</font>
<font color="#009900">// A companion class for making changes</font>
<font color="#009900">// to immutable objects.</font>
<font color="#0000ff">class</font> Mutable {
<font color="#0000ff">private</font> <font color="#0000ff">int</font> data;
<font color="#0000ff">public</font> Mutable(<font color="#0000ff">int</font> initVal) {
data = initVal;
}
<font color="#0000ff">public</font> Mutable add(<font color="#0000ff">int</font> x) {
data += x;
<font color="#0000ff">return</font> <font color="#0000ff">this</font>;
}
<font color="#0000ff">public</font> Mutable multiply(<font color="#0000ff">int</font> x) {
data *= x;
<font color="#0000ff">return</font> <font color="#0000ff">this</font>;
}
<font color="#0000ff">public</font> Immutable2 makeImmutable2() {
<font color="#0000ff">return</font> <font color="#0000ff">new</font> Immutable2(data);
}
}
<font color="#0000ff">public</font> <font color="#0000ff">class</font> Immutable2 {
<font color="#0000ff">private</font> <font color="#0000ff">int</font> data;
<font color="#0000ff">public</font> Immutable2(<font color="#0000ff">int</font> initVal) {
data = initVal;
}
<font color="#0000ff">public</font> <font color="#0000ff">int</font> read() { <font color="#0000ff">return</font> data; }
<font color="#0000ff">public</font> <font color="#0000ff">boolean</font> nonzero() { <font color="#0000ff">return</font> data != 0; }
<font color="#0000ff">public</font> Immutable2 add(<font color="#0000ff">int</font> x) {
<font color="#0000ff">return</font> <font color="#0000ff">new</font> Immutable2(data + x);
}
<font color="#0000ff">public</font> Immutable2 multiply(<font color="#0000ff">int</font> x) {
<font color="#0000ff">return</font> <font color="#0000ff">new</font> Immutable2(data * x);
}
<font color="#0000ff">public</font> Mutable makeMutable() {
<font color="#0000ff">return</font> <font color="#0000ff">new</font> Mutable(data);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -