epubjs
Version:
Render ePub documents in the browser, across many devices
132 lines (130 loc) • 19.4 kB
HTML
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>“There Is No Spoon”</title><link rel="stylesheet" href="core.css" type="text/css"/><meta name="generator" content="DocBook XSL Stylesheets V1.74.0"/></head><body><div class="sect1" title="“There Is No Spoon”"><div class="titlepage"><div><div><h1 class="title"><a id="learnjava3-CHP-8-SECT-3"/>“There Is No Spoon”</h1></div></div></div><p>In the movie <span class="emphasis"><em>The Matrix</em></span>,<sup>[<a id="learnjava3-CHP-8-FNOTE-2" href="#ftn.learnjava3-CHP-8-FNOTE-2" class="footnote">22</a>]</sup> the hero Neo is offered a choice. Take the blue pill and
remain in the world of fantasy, or take the red pill and see things as
they really are. In dealing with generics in Java, we are faced with a
similar ontological dilemma. We can go only so far in any discussion of
generics before we are forced to confront the reality of how they are
implemented. Our fantasy world is one created by the compiler to make our
lives writing code easier to accept. Our reality (though not quite the
dystopian nightmare in the movie) is a harsher place, filled with unseen
dangers and questions. Why don’t casts and tests work properly with
generics? Why can’t I implement what appear to be two different generic
interfaces in one class? Why is it that I can declare an array of generic
types, even though there is no way in Java to create such an array?!?
We’ll answer these questions and more in this chapter, and you won’t even
have to wait for the sequel. Let’s get started.</p><p>The design goals for Java generics were formidable: add a radical
new syntax to the language that safely introduces parameterized types with
no impact on performance and, oh, by the way, make it backward-compatible
with all existing Java code and don’t change the compiled classes in any
serious way. It’s actually quite amazing that these conditions could be
satisfied at all and no surprise that it took a while. But as always,
compromises were required, which lead to some headaches.</p><p>To accomplish this feat, Java employs a technique called <a id="I_indexterm8_id707738" class="indexterm"/><span class="emphasis"><em>erasure</em></span>, which relates to the idea that
since most everything we do with generics applies statically at compile
time, generic information does not need to be carried over into the
compiled classes. The generic nature of the classes, enforced by the
compiler can be “erased” in the compiled classes, which allows us to
maintain compatibility with nongeneric code. While Java does retain
information about the generic features of classes in the compiled form,
this information is used mainly by the compiler. The Java runtime does not
know anything about generics at all.</p><div class="sect2" title="Erasure"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-8-SECT-3.1"/>Erasure</h2></div></div></div><p><a id="idx10398" class="indexterm"/> <a id="idx10407" class="indexterm"/>Let’s take a look at a compiled generic class: our friend,
<code class="literal">List</code>. We can do this easily with the
<span class="emphasis"><em>javap</em></span> command:</p><a id="I_8_tt399"/><pre class="programlisting"> <code class="o">%</code> <strong class="userinput"><code><code class="n">javap</code> <code class="n">java</code><code class="o">.</code><code class="na">util</code><code class="o">.</code><code class="na">List</code></code></strong>
<code class="kd">public</code> <code class="kd">interface</code> <code class="nc">java</code><code class="o">.</code><code class="na">util</code><code class="o">.</code><code class="na">List</code> <code class="kd">extends</code> <code class="n">java</code><code class="o">.</code><code class="na">util</code><code class="o">.</code><code class="na">Collection</code><code class="o">{</code>
<code class="o">...</code>
<code class="kd">public</code> <code class="kd">abstract</code> <code class="kt">boolean</code> <code class="nf">add</code><code class="o">(</code><code class="n">java</code><code class="o">.</code><code class="na">lang</code><code class="o">.</code><code class="na">Object</code><code class="o">);</code>
<code class="kd">public</code> <code class="kd">abstract</code> <code class="n">java</code><code class="o">.</code><code class="na">lang</code><code class="o">.</code><code class="na">Object</code> <code class="nf">get</code><code class="o">(</code><code class="kt">int</code><code class="o">);</code></pre><p>The result looks exactly like it did prior to Java generics, as
you can confirm with any older version of the JDK. Notably, the type of
elements used with the <code class="literal">add()</code> and
<code class="literal">get()</code> methods is <code class="literal">Object</code>. Now, you might think that this is just
a ruse and that when the actual type is instantiated, Java will create a
new version of the class internally. But that’s not the case. This is
the one and only <code class="literal">List</code> class, and it
is the actual runtime type used by all parameterizations of <code class="literal">List</code>; for example, <code class="literal">List<Date></code> and <code class="literal">List<String></code>, as we can confirm:</p><a id="I_8_tt400"/><pre class="programlisting"> <code class="n">List</code><code class="o"><</code><code class="n">Date</code><code class="o">></code> <code class="n">dateList</code> <code class="o">=</code> <code class="k">new</code> <code class="n">ArrayList</code><code class="o"><</code><code class="n">Date</code><code class="o">>();</code>
<code class="n">System</code><code class="o">.</code><code class="na">out</code><code class="o">.</code><code class="na">println</code><code class="o">(</code> <code class="n">dateList</code> <code class="k">instanceof</code> <code class="n">List</code> <code class="o">);</code> <code class="c1">// true!</code></pre><p>But our generic <code class="literal">dateList</code>
clearly does not implement the <code class="literal">List</code>
methods just discussed:</p><a id="I_8_tt401"/><pre class="programlisting"> <code class="n">dateList</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">Object</code><code class="o">()</code> <code class="o">);</code> <code class="c1">// Compile-time Error!</code></pre><p>This illustrates the somewhat schizophrenic nature of Java
generics. The compiler believes in them, but the runtime says they are
an illusion. What if we try something a little more sane and simply
check that our <code class="literal">dateList</code> is a <code class="literal">List<Date></code>:</p><a id="I_8_tt402"/><pre class="programlisting"> <code class="n">System</code><code class="o">.</code><code class="na">out</code><code class="o">.</code><code class="na">println</code><code class="o">(</code> <code class="n">dateList</code> <code class="k">instanceof</code> <code class="n">List</code><code class="o"><</code><code class="n">Date</code><code class="o">></code> <code class="o">);</code> <code class="c1">// Compile-time Error!</code>
<code class="c1">// Illegal, generic type for instanceof</code></pre><p>This time the compiler simply puts its foot down and says, “No.”
You can’t test for a generic type in an <code class="literal">instanceof</code> operation. Since there are no
actual differentiable classes for different parameterizations of
<code class="literal">List</code> at runtime, there is no way for
the <code class="literal">instanceof</code> operator to tell the
difference between one incarnation of <code class="literal">List</code> and another. All of the generic safety
checking was done at compile time and now we’re just dealing with a
single actual <code class="literal">List</code> type.</p><p>What has really happened is that the compiler has erased all of
the angle bracket syntax and replaced the type variables in our <code class="literal">List</code> class with a type that can work at
runtime with any allowed type: in this case, <code class="literal">Object</code>. We would seem to be back where we
started, except that the compiler still has the knowledge to enforce our
usage of the generics in the code at compile time and can, therefore,
handle the cast for us. If you decompile a class using a <code class="literal">List<Date></code> (the
<span class="emphasis"><em>javap</em></span> command with the <a id="I_indexterm8_id707983" class="indexterm"/><span class="emphasis"><em>-c</em></span> option shows you the bytecode, if
you dare), you will see that the compiled code actually contains the
cast to <code class="literal">Date</code>, even though we didn’t
write it ourselves.</p><p>We can now answer one of the questions we posed at the beginning
of the section (“Why can’t I implement what appear to be two different
generic interfaces in one class?”). We can’t have a class that
implements two different generic <code class="literal">List</code>
instantiations because they are really the same type at runtime and
there is no way to tell them apart:<a id="I_indexterm8_id708014" class="indexterm"/><a id="I_indexterm8_id708021" class="indexterm"/></p><a id="I_8_tt403"/><pre class="programlisting"> <code class="kd">public</code> <code class="kd">abstract</code> <code class="kd">class</code> <code class="nc">DualList</code> <code class="kd">implements</code> <code class="n">List</code><code class="o"><</code><code class="n">String</code><code class="o">>,</code> <code class="n">List</code><code class="o"><</code><code class="n">Date</code><code class="o">></code> <code class="o">{</code> <code class="o">}</code>
<code class="c1">// Error: java.util.List cannot be inherited with different arguments:</code>
<code class="c1">// <java.lang.String> and <java.util.Date></code></pre></div><div class="sect2" title="Raw Types"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-8-SECT-3.2"/>Raw Types</h2></div></div></div><p><a id="idx10418" class="indexterm"/> <a id="idx10434" class="indexterm"/>Although the compiler treats different parameterizations
of a generic type as different types (with different APIs) at compile
time, we have seen that only one real type exists at runtime. For
example, the class of <code class="literal">List<Date></code> and <code class="literal">List<String></code> share the plain old Java
class <code class="literal">List</code>. <code class="literal">List</code> is called the <span class="emphasis"><em>raw
type</em></span> of the generic class. Every generic has a raw type. It
is the degenerate, “plain” Java form from which all of the generic type
information has been removed and the type variables replaced by a
general Java type like <code class="literal">Object</code>.<sup>[<a id="id1078506" href="#ftn.id1078506" class="footnote">23</a>]</sup></p><p>It is still possible to use raw types in Java just as before
generics were added to the language. The only difference is that the
Java compiler generates a warning wherever they are used in an “unsafe”
way. For example:</p><a id="I_8_tt404"/><pre class="programlisting"> <code class="c1">// nongeneric Java code using the raw type</code>
<code class="n">List</code> <code class="n">list</code> <code class="o">=</code> <code class="k">new</code> <code class="n">ArrayList</code><code class="o">();</code> <code class="c1">// assignment ok</code>
<code class="n">list</code><code class="o">.</code><code class="na">add</code><code class="o">(</code><code class="s">"foo"</code><code class="o">);</code> <code class="c1">// Compiler warning on usage of raw type</code></pre><p>This snippet uses the raw <code class="literal">List</code>
type just as old-fashioned Java code prior to Java 5 would have. The
difference is that now the Java compiler issues an <span class="emphasis"><em>unchecked
warning</em></span> about the code if we attempt to insert an object into
the list.</p><a id="I_8_tt405"/><pre class="programlisting"> <code class="o">%</code> <strong class="userinput"><code><code class="n">javac</code> <code class="n">MyClass</code><code class="o">.</code><code class="na">java</code></code></strong>
<code class="nl">Note:</code> <code class="n">MyClass</code><code class="o">.</code><code class="na">java</code> <code class="n">uses</code> <code class="n">unchecked</code> <code class="n">or</code> <code class="n">unsafe</code> <code class="n">operations</code><code class="o">.</code>
<code class="nl">Note:</code> <code class="n">Recompile</code> <code class="n">with</code> <code class="o">-</code><code class="nl">Xlint:</code><code class="n">unchecked</code> <code class="k">for</code> <code class="n">details</code><code class="o">.</code></pre><p>The compiler instructs us to use the <a id="I_indexterm8_id708180" class="indexterm"/><code class="literal">-Xlint:unchecked</code> option
to get more specific information about the locations of unsafe
operations:</p><a id="I_8_tt406"/><pre class="programlisting"> <code class="o">%</code> <strong class="userinput"><code><code class="n">javac</code> <code class="o">-</code><code class="nl">Xlint:</code><code class="n">unchecked</code> <code class="n">MyClass</code><code class="o">.</code><code class="na">java</code></code></strong>
<code class="nl">warning:</code> <code class="o">[</code><code class="n">unchecked</code><code class="o">]</code> <code class="n">unchecked</code> <code class="n">call</code> <code class="n">to</code> <code class="n">add</code><code class="o">(</code><code class="n">E</code><code class="o">)</code> <code class="n">as</code> <code class="n">a</code> <code class="n">member</code> <code class="n">of</code> <code class="n">the</code> <code class="n">raw</code> <code class="n">type</code>
<code class="n">java</code><code class="o">.</code><code class="na">util</code><code class="o">.</code>
<code class="nl">List:</code> <code class="n">list</code><code class="o">.</code><code class="na">add</code><code class="o">(</code><code class="s">"foo"</code><code class="o">);</code></pre><p>Note that creating and assigning the raw <code class="literal">ArrayList</code> does not generate a warning. It is
only when we try to use an “unsafe” method (one that refers to a type
variable) that we get the warning. This means that it’s still OK to use
older-style, nongeneric Java APIs that work with raw types. We only get
warnings when we do something unsafe in our own code.</p><p>One more thing about erasure before we move on. In the previous
examples, the type variables were replaced by the <code class="literal">Object</code> type, which could represent any type
applicable to the type variable <code class="literal">E</code>.
Later we’ll see that this is not always the case. We can place
limitations or <a id="I_indexterm8_id708239" class="indexterm"/><a id="I_indexterm8_id708248" class="indexterm"/><span class="emphasis"><em>bounds</em></span> on the parameter types, and,
when we do, the compiler can be more restrictive about the erasure of
the type. We’ll explain in more detail later after we discuss bounds,
but, for example:</p><a id="I_8_tt407"/><pre class="programlisting"> <code class="kd">class</code> <code class="nc">Bounded</code><code class="o"><</code> <code class="n">E</code> <code class="kd">extends</code> <code class="n">Date</code> <code class="o">></code> <code class="o">{</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">addElement</code><code class="o">(</code> <code class="n">E</code> <code class="n">element</code> <code class="o">)</code> <code class="o">{</code> <code class="o">...</code> <code class="o">}</code>
<code class="o">}</code></pre><p>This parameter type declaration says that the element type
<code class="literal">E</code> must be a subtype of the <code class="literal">Date</code> type. In this case, the erasure of the
<code class="literal">addElement()</code> method is therefore more
restrictive than <code class="literal">Object</code>, and the
compiler uses <code class="literal">Date</code>:</p><a id="I_8_tt408"/><pre class="programlisting"> <code class="kd">public</code> <code class="kt">void</code> <code class="nf">addElement</code><code class="o">(</code> <code class="n">Date</code> <code class="n">element</code> <code class="o">)</code> <code class="o">{</code> <code class="o">...</code> <code class="o">}</code></pre><p><a id="I_indexterm8_id708311" class="indexterm"/> <code class="literal">Date</code> is called the
<span class="emphasis"><em>upper bound</em></span> of this type, meaning that it is the
top of the object hierarchy here and the type can be instantiated only
on type <code class="literal">Date</code> or on “lower” (more
derived) types.</p><p>Now that we have a handle on what generic types really are, we can
go into a little more detail about how they behave.<a id="I_indexterm8_id708336" class="indexterm"/><a id="I_indexterm8_id708343" class="indexterm"/></p></div><div class="footnotes"><br/><hr/><div class="footnote"><p><sup>[<a id="ftn.learnjava3-CHP-8-FNOTE-2" href="#learnjava3-CHP-8-FNOTE-2" class="para">22</a>] </sup>For those of you who might like some context for the title of
this section, here is where it comes from:</p><p>Boy: Do not try and bend the spoon. That’s impossible. Instead,
only try to realize the truth.</p><p>Neo: What truth?</p><p>Boy: There is no spoon.</p><p>Neo: There is no spoon?</p><p>Boy: Then you’ll see that it is not the spoon that bends, it is
only yourself.</p><p>—Wachowski, Andy and Larry. <span class="emphasis"><em>The Matrix</em></span>. 136
minutes. Warner Brothers, 1999.</p></div><div class="footnote"><p><sup>[<a id="ftn.id1078506" href="#id1078506" class="para">23</a>] </sup><a id="I_indexterm8_id708108" class="indexterm"/>When generics were added in Java 5.0, things were
carefully arranged such that the raw type of all of the generic
classes worked out to be exactly the same as the earlier, nongeneric
types. So the raw type of a <code class="literal">List</code>
in Java 5.0 is the same as the old, nongeneric <code class="literal">List</code> type that had been around since JDK
1.2. Since the vast majority of current Java code at the time did
not use generics, this type equivalency and compatibility was very
important.</p></div></div></div></body></html>