UNPKG

epubjs

Version:

Render ePub documents in the browser, across many devices

132 lines (130 loc) 19.4 kB
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <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&lt;Date&gt;</code> and <code class="literal">List&lt;String&gt;</code>, as we can confirm:</p><a id="I_8_tt400"/><pre class="programlisting"> <code class="n">List</code><code class="o">&lt;</code><code class="n">Date</code><code class="o">&gt;</code> <code class="n">dateList</code> <code class="o">=</code> <code class="k">new</code> <code class="n">ArrayList</code><code class="o">&lt;</code><code class="n">Date</code><code class="o">&gt;();</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&lt;Date&gt;</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">&lt;</code><code class="n">Date</code><code class="o">&gt;</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&lt;Date&gt;</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">&lt;</code><code class="n">String</code><code class="o">&gt;,</code> <code class="n">List</code><code class="o">&lt;</code><code class="n">Date</code><code class="o">&gt;</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">// &lt;java.lang.String&gt; and &lt;java.util.Date&gt;</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&lt;Date&gt;</code> and <code class="literal">List&lt;String&gt;</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">&lt;</code> <code class="n">E</code> <code class="kd">extends</code> <code class="n">Date</code> <code class="o">&gt;</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>