📄 tij0129.html
字号:
<font color="#0000ff">public</font> <font color="#0000ff">static</font> Immutable2 modify1(Immutable2 y){
Immutable2 val = y.add(12);
val = val.multiply(3);
val = val.add(11);
val = val.multiply(2);
<font color="#0000ff">return</font> val;
}
<font color="#009900">// This produces the same result:</font>
<font color="#0000ff">public</font> <font color="#0000ff">static</font> Immutable2 modify2(Immutable2 y){
Mutable m = y.makeMutable();
m.add(12).multiply(3).add(11).multiply(2);
<font color="#0000ff">return</font> m.makeImmutable2();
}
<font color="#0000ff">public</font> <font color="#0000ff">static</font> <font color="#0000ff">void</font> main(String[] args) {
Immutable2 i2 = <font color="#0000ff">new</font> Immutable2(47);
Immutable2 r1 = modify1(i2);
Immutable2 r2 = modify2(i2);
System.out.println("i2 = " + i2.read());
System.out.println("r1 = " + r1.read());
System.out.println("r2 = " + r2.read());
}
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Immutable2
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">contains
methods that, as before, preserve the immutability of the objects by producing
new objects whenever a modification is desired. These are the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>add( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
and
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>multiply( )
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">methods.
The companion class is called
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Mutable</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
and it also has
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>add( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
and
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>multiply( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
methods, but these modify the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Mutable</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
object rather than making a new one. In addition,
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Mutable</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
has a method to use its data to produce an
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Immutable2</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
object and vice versa.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
two static methods
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>modify1( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
and
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>modify2( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
show two different approaches to producing the same result. In
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>modify1( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
everything is done within the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Immutable2</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
class and you can see that four new
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Immutable2</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
objects are created in the process. (And each time
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>val</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
is reassigned, the previous object becomes garbage.)
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">In
the method
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>modify2( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
you can see that the first action is to take the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Immutable2
y
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
and produce a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Mutable</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
from it. (This is just like calling
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>clone( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
as you saw earlier, but this time a different type of object is created.) Then
the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Mutable</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
object is used to perform a lot of change operations
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>without</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
requiring the creation of many new objects. Finally, it’s turned back
into an
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Immutable2</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
Here, two new objects are created (the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Mutable</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
and the result
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Immutable2</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">)
instead of four.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">This
approach makes sense, then, when:
</FONT><P></DIV>
<OL>
<LI><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"> You
need immutable objects and
</FONT><LI><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"> You
often need to make a lot of modifications or
</FONT><LI><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"> It’s
expensive to create new immutable objects
</FONT><a name="_Toc408018672"></a></OL><A NAME="Heading386"></A><H3 ALIGN=LEFT>
Immutable
Strings
<P><A NAME="Index1494"></A><A NAME="Index1495"></A></H3>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Consider
the following code:
</FONT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: Stringer.java</font>
<font color="#0000ff">public</font> <font color="#0000ff">class</font> Stringer {
<font color="#0000ff">static</font> String upcase(String s) {
<font color="#0000ff">return</font> s.toUpperCase();
}
<font color="#0000ff">public</font> <font color="#0000ff">static</font> <font color="#0000ff">void</font> main(String[] args) {
String q = <font color="#0000ff">new</font> String("howdy");
System.out.println(q); <font color="#009900">// howdy</font>
String qq = upcase(q);
System.out.println(qq); <font color="#009900">// HOWDY</font>
System.out.println(q); <font color="#009900">// howdy</font>
}
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">When
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>q</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
is passed in to
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>upcase( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
it’s actually a copy of the handle to
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>q</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
The object this handle is connected to stays put in a single physical location.
The handles are copied as they are passed around.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Looking
at the definition for
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>upcase( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
you can see that the handle that’s passed in has the name
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>s</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
and it exists for only as long as the body of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>upcase( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
is being executed. When
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>upcase( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
completes, the local handle
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>s</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
vanishes.
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>upcase( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
returns the result, which is the original string with all the characters set to
uppercase. Of course, it actually returns a handle to the result. But it turns
out that the handle that it returns is for a new object, and the original
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>q</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
is left alone. How does this happen?
</FONT><P></DIV>
<A NAME="Heading387"></A><H4 ALIGN=LEFT>
Implicit
constants
<P><A NAME="Index1496"></A></H4>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">If
you say:
</FONT><P></DIV><DIV ALIGN=LEFT><TT><FONT FACE="Courier New" SIZE=3 COLOR="Black">String
s = "asdf";
</FONT></TT><P><TT><FONT FACE="Courier New" SIZE=3 COLOR="Black">String
x = Stringer.upcase(s);
</FONT></TT><P></DIV><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">do
you really want the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>upcase( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method to
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>change</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
the argument? In general, you don’t, because an argument usually looks to
the reader of the code as a piece of information provided to the method, not
something to be modified. This is an important guarantee, since it makes code
easier to write and understand.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">In
C++, the availability of this guarantee was important enough to put in a
special keyword, <A NAME="Index1497"></A></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>const</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
to allow the programmer to ensure that a handle (pointer or reference in C++)
could not be used to modify the original object. But then the C++ programmer
was required to be diligent and remember to use
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>const</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
everywhere. It can be confusing and easy to forget.
</FONT><a name="_Toc375545435"></a><P></DIV>
<A NAME="Heading388"></A><H4 ALIGN=LEFT>
Overloading
‘+’ and the StringBuffer
</H4>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Objects
of 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 are designed to be immutable, using the technique shown previously. If
you examine the online documentation
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">for
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 (which is summarized a little later in this chapter), you’ll see
that every method in the class that appears to modify a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>String</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
really creates and returns a brand new
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>String</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
object containing the modification. The original
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>String</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
is left untouched. Thus, there’s no feature in Java like C++’s
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>const</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
to make the compiler support the immutability of your objects. If you want it,
you have to wire it in yourself, like
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>String</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
does.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Since
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>String
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">objects
are immutable, you can <A NAME="Index1498"></A>alias
to a particular
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>String
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">as
many times as you want. Because it’s read-only there’s no
possibility that one handle will change something that will affect the other
handles. So a read-only object solves the aliasing problem nicely.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">It
also seems possible to handle all the cases in which you need a modified object
by creating a brand new version of the object with the modifications, as
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>String</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
does. However, for some operations this isn’t efficient. A case in point
is the <A NAME="Index1499"></A><A NAME="Index1500"></A>operator
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>‘+</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">’
that has been overloaded for
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>String</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
objects. <A NAME="Index1501"></A><A NAME="Index1502"></A>Overloading
means that it has been given an extra meaning when used with a particular
class. (The ‘
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>+</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">’
and ‘
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>+=</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">‘
for
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>String</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
are the only operators that are overloaded in Java and Java does not allow the
programmer to overload any others
</FONT><A NAME="fnB52" HREF="#fn52">[52]</A><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">).</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">When
used with
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>String</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
objects, the ‘
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>+</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">’
allows you to concatenate
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>String</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">s
together:
</FONT><P></DIV><DIV ALIGN=LEFT><TT><FONT FACE="Courier New" SIZE=3 COLOR="Black">String
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -