⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chapter01.html

📁 java 是一个很好的网络开发环境。由于它是通过解释的方法
💻 HTML
📖 第 1 页 / 共 5 页
字号:
object receives a particular message. If you simply inherit a class and
don’t do anything else, the methods from the base-class interface come
right along into the derived class. That means objects of the derived class have
not only the same type, they also have the same behavior, which doesn’t
seem particularly interesting.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You have two ways to differentiate
your new derived class from the original base class it inherits from. The first
is quite straightforward: you simply add brand new functions to the derived
class. These new functions are not part of the base class interface. This means
that the base class simply didn&#8217;t do as much as you wanted it to, so you
add more functions. This simple and primitive use for inheritance is, at times,
the perfect solution to your problem. However, you should look closely for the
possibility that your base class might need these additional
functions.</FONT><A NAME="_Toc375545193"></A><A NAME="_Toc408018390"></A><BR></P></DIV>
<A NAME="Heading23"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Overriding base-class functionality</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Although the <B>extends</B> keyword
implies that you are going to add new functions to the interface, that&#8217;s
not necessarily true. The second way to differentiate your new class is to
<I>change</I> the behavior of an existing base-class function. This is referred
to as <I>overriding</I> that function.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To override a function, you simply
create a new definition for the function in the derived class. You&#8217;re
saying &#8220;I&#8217;m using the same interface function here, but I want it to
do something different for my new
type.&#8221;</FONT><A NAME="_Toc375545194"></A><A NAME="_Toc408018391"></A><BR></P></DIV>
<A NAME="Heading24"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Is-a vs. is-like-a relationships</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There&#8217;s a certain debate that
can occur about inheritance: Should inheritance override <I>only</I> base-class
functions? This means that the derived type is <I>exactly</I> the same type as
the base class since it has exactly the same interface. As a result, you can
exactly substitute an object of the derived class for an object of the base
class. This can be thought of as <I>pure substitution</I>. In a sense, this is
the ideal way to treat inheritance. We often refer to the relationship between
the base class and derived classes in this case as an <I>is-a</I> relationship,
because you can say &#8220;a circle <I>is a</I> shape.&#8221; A test for
inheritance is whether you can state the is-a relationship about the classes and
have it make sense.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There are times when you must add
new interface elements to a derived type, thus extending the interface and
creating a new type. The new type can still be substituted for the base type,
but the substitution isn&#8217;t perfect in a sense because your new functions
are not accessible from the base type. This can be described as an
<I>is-like-a</I> relationship; the new type has the interface of the old type
but it also contains other functions, so you can&#8217;t really say it&#8217;s
exactly the same. For example, consider an air conditioner. Suppose your house
is wired with all the controls for cooling; that is, it has an interface that
allows you to control cooling. Imagine that the air conditioner breaks down and
you replace it with a heat pump, which can both heat and cool. The heat pump
<I>is-like-an</I> air conditioner, but it can do more. Because your house is
wired only to control cooling, it is restricted to communication with the
cooling part of the new object. The interface of the new object has been
extended, and the existing system doesn&#8217;t know about anything except the
original interface.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When you see the substitution
principle it&#8217;s easy to feel like that&#8217;s the only way to do things,
and in fact it is nice if your design works out that way. But you&#8217;ll find
that there are times when it&#8217;s equally clear that you must add new
functions to the interface of a derived class. With inspection both cases should
be reasonably
obvious.</FONT><A NAME="_Toc375545195"></A><A NAME="_Toc408018392"></A><BR></P></DIV>
<A NAME="Heading25"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Interchangeable objects <BR>with polymorphism</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Inheritance usually ends up
creating a family of classes, all based on the same uniform interface. We
express this with an inverted tree
diagram:</FONT><A NAME="fnB5" HREF="#fn5">[5]</A><BR></P></DIV>
<DIV ALIGN="CENTER"><FONT FACE="Georgia"><IMG SRC="Tjava105.gif"></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">One of the most important things
you do with such a family of classes is to treat an object of a derived class as
an object of the base class. This is important because it means you can write a
single piece of code that ignores the specific details of type and talks just to
the base class. That code is then <I>decoupled</I> from type-specific
information, and thus is simpler to write and easier to understand. And, if a
new type &#8211; a <B>Triangle</B>, for example &#8211;<B> </B>is added through
inheritance, the code you write will work just as well for the new type of
<B>Shape</B> as it did on the existing types. Thus the program is
<I>extensible</I>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Consider the above example. If you
write a function in Java:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>void</font> doStuff(Shape s) {
  s.erase();
  <font color=#009900>// ...</font>
  s.draw();
}</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This function speaks to any
<B>Shape</B>, so it is independent of the specific type of object it&#8217;s
drawing and erasing. If in some other program we use the <B>doStuff(&#160;)</B>
function:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE>Circle c = <font color=#0000ff>new</font> Circle();
Triangle t = <font color=#0000ff>new</font> Triangle();
Line l = <font color=#0000ff>new</font> Line();
doStuff(c);
doStuff(t);
doStuff(l);</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The calls to <B>doStuff(&#160;)
</B>automatically work right, regardless of the exact type of the object.
</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This is actually a pretty amazing
trick. Consider the line:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE>doStuff(c);</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">What&#8217;s happening here is that
a <B>Circle</B> handle is being passed into a function that&#8217;s expecting a
<B>Shape</B> handle. Since a <B>Circle</B> <I>is</I> a <B>Shape</B> it can be
treated as one by <B>doStuff(&#160;)</B>. That is, any message that
<B>doStuff(&#160;)</B> can send to a <B>Shape</B>, a <B>Circle</B> can accept.
So it is a completely safe and logical thing to do.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">We call this process of treating a
derived type as though it were its base type <I>upcasting</I>. The name <I>cast
</I>is used in the sense of casting into a mold and the <I>up</I> comes from the
way the inheritance diagram is typically arranged, with the base type at the top
and the derived classes fanning out downward. Thus, casting to a base type is
moving up the inheritance diagram: upcasting.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">An object-oriented program contains
some upcasting somewhere, because that&#8217;s how you decouple yourself from
knowing about the exact type you&#8217;re working with. Look at the code in
<B>doStuff(&#160;)</B>:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE>  s.erase();
  <font color=#009900>// ...</font>
  s.draw();</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Notice that it doesn&#8217;t say
&#8220;If you&#8217;re a <B>Circle</B>, do this, if you&#8217;re a
<B>Square</B>, do that, etc.&#8221; If you write that kind of code, which checks
for all the possible types a <B>Shape</B> can actually be, it&#8217;s messy and
you need to change it every time you add a new kind of <B>Shape</B>. Here, you
just say &#8220;You&#8217;re a shape, I know you can <B>erase(&#160;)
</B>yourself, do it and take care of the details correctly.&#8221;
</FONT><A NAME="_Toc375545196"></A><A NAME="_Toc408018393"></A><BR></P></DIV>
<A NAME="Heading26"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Dynamic binding</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">What&#8217;s amazing about the code
in <B>doStuff(&#160;)</B> is that somehow the right thing happens. Calling
<B>draw(&#160;)</B> for <B>Circle</B> causes different code to be executed than
when calling <B>draw(&#160;) </B>for a <B>Square</B> or a <B>Line</B>, but when
the <B>draw(&#160;)</B> message is sent to an anonymous <B>Shape</B>, the
correct behavior occurs based on the actual type that the <B>Shape</B> handle
happens to be connected to. This is amazing because when the Java compiler is
compiling the code for <B>doStuff(&#160;)</B>, it cannot know exactly what types
it is dealing with. So ordinarily, you&#8217;d expect it to end up calling the
version of <B>erase(&#160;)</B> for <B>Shape</B>, and <B>draw(&#160;)</B> for
<B>Shape</B> and not for the specific <B>Circle</B>, <B>Square</B>, or
<B>Line</B>. And yet the right thing happens. Here&#8217;s how it
works.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When you send a message to an
object even though you don&#8217;t know what specific type it is, and the right
thing happens, that&#8217;s called <I>polymorphism</I>. The process used by
object-oriented programming languages to implement polymorphism is called
<I>dynamic binding</I>. The compiler and run-time system handle the details; all
you need to know is that it happens and more importantly how to design with
it.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Some languages require you to use a
special keyword to enable dynamic binding. In C++ this keyword is
<B>virtual</B>. In Java, you never need to remember to add a keyword because
functions are automatically dynamically bound. So you can always expect that
when you send a message to an object, the object will do the right thing, even
when upcasting is
involved.</FONT><A NAME="_Toc375545197"></A><A NAME="_Toc408018394"></A><BR></P></DIV>
<A NAME="Heading27"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Abstract base classes and interfaces</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Often in a design, you want the
base class to present <I>only</I> an interface for its derived classes. That is,
you don&#8217;t want anyone to actually create an object of the base class, only
to upcast to it so that its interface can be used. This is accomplished by
making that class <I>abstract </I>using the <B>abstract</B> keyword. If anyone
tries to make an object of an <B>abstract</B> class, the compiler prevents them.
This is a tool to enforce a particular design.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can also use the
<B>abstract</B> keyword to describe a method that hasn&#8217;t been implemented
yet &#8211; as a stub indicating &#8220;here is an interface function for all
types inherited from this class, but at this point I don&#8217;t have any
implementation for it.&#8221; An <B>abstract </B>method may be created only
inside an <B>abstract </B>class. When the class is inherited, that method must
be implemented, or the inherited class becomes <B>abstract</B> as well. Creating
an <B>abstract</B> method allows you to put a method in an interface without
being forced to provide a possibly meaningless body of code for that
method.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>interface</B> keyword takes
the concept of an <B>abstract</B> class one step further by preventing any
function definitions at all. The <B>interface</B> is a very useful and
commonly-used tool, as it provides the perfect separation of interface and
implementation. In addition, you can combine many interfaces together, if you
wish. (You cannot inherit from more than one regular <B>class</B> or <B>abstract
class</B>.)</FONT><A NAME="_Toc375545198"></A><A NAME="_Toc408018395"></A><BR></P></DIV>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -