UNPKG

epubjs

Version:

Render ePub documents in the browser, across many devices

89 lines (88 loc) 14 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>Parameterized Type Relationships</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="Parameterized Type Relationships"><div class="titlepage"><div><div><h1 class="title"><a id="learnjava3-CHP-8-SECT-4"/>Parameterized Type Relationships</h1></div></div></div><p><a id="idx10417" class="indexterm"/> <a id="idx10433" class="indexterm"/>We know now that parameterized types share a common, raw type. This is why our parameterized <code class="literal">List&lt;Date&gt;</code> is just a <code class="literal">List</code> at runtime. In fact, we can assign any instantiation of <code class="literal">List</code> to the raw type if we want:</p><a id="I_8_tt409"/><pre class="programlisting"> <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">&lt;</code><code class="n">Date</code><code class="o">&gt;();</code></pre><p>We can even go the other way and assign a raw type to a specific instantiation of the generic type:</p><a id="I_8_tt410"/><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">dates</code> <code class="o">=</code> <code class="k">new</code> <code class="n">ArrayList</code><code class="o">();</code> <code class="c1">// unchecked warning</code></pre><p>This statement generates an unchecked warning on the assignment, but thereafter the compiler trusts that the list contained only <code class="literal">Date</code>s prior to the assignment. It is also permissible, albeit pointless, to perform a cast in this statement. We’ll talk about casting to generic types a bit later.</p><p>Whatever the runtime types, the compiler is running the show and does not let us assign things that are clearly incompatible:</p><a id="I_8_tt411"/><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">dates</code> <code class="o">=</code> <code class="k">new</code> <code class="n">ArrayList</code><code class="o">&lt;</code><code class="n">String</code><code class="o">&gt;();</code> <code class="c1">// Compile-time Error!</code></pre><p>Of course, the <code class="literal">ArrayList&lt;String&gt;</code> does not implement the methods of <code class="literal">List&lt;Date&gt;</code> conjured by the compiler, so these types are incompatible.</p><p>But what about more interesting type relationships? The <code class="literal">List</code> interface, for example, is a subtype of the more general <code class="literal">Collection</code> interface. Is a particular instantiation of the generic <code class="literal">List</code> also assignable to some instantiation of the generic <code class="literal">Collection</code>? Does it depend on the type parameters and their relationships? Clearly, a <code class="literal">List&lt;Date&gt;</code> is not a <code class="literal">Collection&lt;String&gt;</code>. But is a <code class="literal">List&lt;Date&gt;</code> a <code class="literal">Collection&lt;Date&gt;</code>? Can a <code class="literal">List&lt;Date&gt;</code> be a <code class="literal">Collection&lt;Object&gt;</code>?</p><p><a id="I_indexterm8_id708520" class="indexterm"/>We’ll just blurt out the answer first, then walk through it and explain. The rule is that for the simple types of generic instantiations we’ve discussed so far, <span class="emphasis"><em>inheritance applies only to the “base” generic type and not to the parameter types</em></span>. Furthermore, assignability applies only when the two generic types are instantiated on <span class="emphasis"><em>exactly the same parameter type</em></span>. In other words, there is still one-dimensional inheritance, following the base generic class type, but with the additional restriction that the parameter types must be identical.</p><p>For example, recalling that a <code class="literal">List</code> is a type of <code class="literal">Collection</code>, we can assign instantiations of <code class="literal">List</code> to instantiations of <code class="literal">Collection</code> when the type parameter is exactly the same:</p><a id="I_8_tt412"/><pre class="programlisting"> <code class="n">Collection</code><code class="o">&lt;</code><code class="n">Date</code><code class="o">&gt;</code> <code class="n">cd</code><code class="o">;</code> <code class="n">List</code><code class="o">&lt;</code><code class="n">Date</code><code class="o">&gt;</code> <code class="n">ld</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">cd</code> <code class="o">=</code> <code class="n">ld</code><code class="o">;</code> <code class="c1">// Ok!</code></pre><p>This code snippet says that a <code class="literal">List&lt;Date&gt;</code> is a <code class="literal">Collection&lt;Date&gt;</code>—pretty intuitive. But trying the same logic on a variation in the parameter types fails:</p><a id="I_8_tt413"/><pre class="programlisting"> <code class="n">List</code><code class="o">&lt;</code><code class="n">Object</code><code class="o">&gt;</code> <code class="n">lo</code><code class="o">;</code> <code class="n">List</code><code class="o">&lt;</code><code class="n">Date</code><code class="o">&gt;</code> <code class="n">ld</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">lo</code> <code class="o">=</code> <code class="n">ld</code><code class="o">;</code> <code class="c1">// Compile-time Error! Incompatible types.</code></pre><p>Although our intuition tells us that the <code class="literal">Date</code>s in that <code class="literal">List</code> could all live happily as <code class="literal">Object</code>s in a <code class="literal">List</code>, the assignment is an error. We’ll explain precisely why in the next section, but for now just note that the type parameters are not exactly the same and that there is no inheritance relationship among parameter types in generics. This is a case where thinking of the instantiation in terms of types and not in terms of what they do helps. These are not really a “list of dates” and a “list of objects,” but more like a <code class="literal">DateList</code> and an <code class="literal">ObjectList</code>, the relationship of which is not immediately obvious.</p><p>Try to pick out what’s OK and what’s not OK in the following example:</p><a id="I_8_tt414"/><pre class="programlisting"> <code class="n">Collection</code><code class="o">&lt;</code><code class="n">Number</code><code class="o">&gt;</code> <code class="n">cn</code><code class="o">;</code> <code class="n">List</code><code class="o">&lt;</code><code class="n">Integer</code><code class="o">&gt;</code> <code class="n">li</code> <code class="o">=</code> <code class="k">new</code> <code class="n">ArrayList</code><code class="o">&lt;</code><code class="n">Integer</code><code class="o">&gt;();</code> <code class="n">cn</code> <code class="o">=</code> <code class="n">li</code><code class="o">;</code> <code class="c1">// Compile-time Error! Incompatible types.</code></pre><p>It is possible for an instantiation of <code class="literal">List</code> to be an instantiation of <code class="literal">Collection</code>, but only if the parameter types are exactly the same. Inheritance doesn’t follow the parameter types and this example fails.</p><p>One more thing: earlier we mentioned that this rule applies to the simple types of instantiations we’ve discussed so far in this chapter. What other types are there? Well, the kinds of instantiations we’ve seen so far where we plug in an actual Java type as a parameter are called <span class="emphasis"><em>concrete type instantiations</em></span>. Later we’ll talk about <span class="emphasis"><em>wildcard instantiations</em></span>, which are akin to mathematical set operations on types. We’ll see that it’s possible to make more exotic instantiations of generics where the type relationships are actually two-dimensional, depending both on the base type and the parameterization. But don’t worry: this doesn’t come up very often and is not as scary as it sounds.</p><div class="sect2" title="Why Isn’t a List&lt;Date&gt; a List&lt;Object&gt;?"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-8-SECT-4.1"/>Why Isn’t a List&lt;Date&gt; a List&lt;Object&gt;?</h2></div></div></div><p><a id="idx10425" class="indexterm"/> <a id="idx10426" class="indexterm"/>It’s a reasonable question. Even with our brains thinking of arbitrary <code class="literal">DateList</code> and <code class="literal">ObjectList</code> types, we can still ask why they couldn’t be assignable. Why shouldn’t we be able to assign our <code class="literal">List&lt;Date&gt;</code> to a <code class="literal">List&lt;Object&gt;</code> and work with the <code class="literal">Date</code> elements as <code class="literal">Object</code> types?</p><p>The reason gets back to the heart of the rationale for generics that we discussed in the introduction: changing APIs. In the simplest case, supposing an <code class="literal">ObjectList</code> type extends a <code class="literal">DateList</code> type, the <code class="literal">DateList</code> would have all of the methods of <code class="literal">ObjectList</code> and we could still insert <code class="literal">Object</code>s into it. Now, you might object that generics let us change the APIs, so that doesn’t apply anymore. That’s true, but there is a bigger problem. If we could assign our <code class="literal">DateList</code> to an <code class="literal">ObjectList</code> variable, we would have to be able to use <code class="literal">Object</code> methods to insert elements of types other than <code class="literal">Date</code> into it. We could <span class="emphasis"><em>alias</em></span> the <code class="literal">DateList</code> as an <code class="literal">ObjectList</code> and try to trick it into accepting some other type:</p><a id="I_8_tt415"/><pre class="programlisting"> <code class="n">DateList</code> <code class="n">dateList</code> <code class="o">=</code> <code class="k">new</code> <code class="n">DateList</code><code class="o">();</code> <code class="n">ObjectList</code> <code class="n">objectList</code> <code class="o">=</code> <code class="n">dateList</code><code class="o">;</code> <code class="c1">// Can't really do this</code> <code class="n">objectList</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">Foo</code><code class="o">()</code> <code class="o">);</code> <code class="c1">// should be runtime error!</code></pre><p>We’d expect to get a runtime error when the actual <a id="I_indexterm8_id708848" class="indexterm"/><code class="literal">DateList</code> implementation was presented with the wrong type of object. And therein lies the problem. Java generics have no runtime representation. Even if this functionality were useful, there is no way with the current scheme for Java to know what to do at runtime. Another way to look at it is that this feature is simply dangerous because it allows for an error at runtime that couldn’t be caught at compile time. In general, we’d like to catch type errors at compile time. By disallowing these assignments, Java can guarantee that your code is typesafe if it compiles with no unchecked warnings.</p><p>Actually, that last sentence is not entirely true, but it doesn’t have to do with generics; it has to do with arrays. If this all sounds familiar to you, it’s because we mentioned it previously in relation to Java arrays. Array types have an inheritance relationship that allows this kind of aliasing to occur:</p><a id="I_8_tt416"/><pre class="programlisting"> <code class="n">Date</code> <code class="o">[]</code> <code class="n">dates</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Date</code><code class="o">[</code><code class="mi">10</code><code class="o">];</code> <code class="n">Object</code> <code class="o">[]</code> <code class="n">objects</code> <code class="o">=</code> <code class="n">dates</code><code class="o">;</code> <code class="n">objects</code><code class="o">[</code><code class="mi">0</code><code class="o">]</code> <code class="o">=</code> <code class="s">"not a date"</code><code class="o">;</code> <code class="c1">// Runtime ArrayStoreException!</code></pre><p>However, arrays have runtime representations as different classes and they check themselves at runtime, throwing an <code class="literal">ArrayStoreException</code> in just this case. So in theory, Java code is not guaranteed typesafe by the compiler if you use arrays in this way.<a id="I_indexterm8_id708897" class="indexterm"/><a id="I_indexterm8_id708904" class="indexterm"/><a id="I_indexterm8_id708911" class="indexterm"/><a id="I_indexterm8_id708918" class="indexterm"/></p></div></div></body></html>