epubjs
Version:
Render ePub documents in the browser, across many devices
89 lines (88 loc) • 14 kB
HTML
<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<Date></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"><</code><code class="n">Date</code><code class="o">>();</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"><</code><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">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"><</code><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">ArrayList</code><code class="o"><</code><code class="n">String</code><code class="o">>();</code> <code class="c1">// Compile-time Error!</code></pre><p>Of course, the <code class="literal">ArrayList<String></code> does not implement the
methods of <code class="literal">List<Date></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<Date></code> is not a <code class="literal">Collection<String></code>. But is a <code class="literal">List<Date></code> a <code class="literal">Collection<Date></code>? Can a <code class="literal">List<Date></code> be a <code class="literal">Collection<Object></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"><</code><code class="n">Date</code><code class="o">></code> <code class="n">cd</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="n">ld</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">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<Date></code> is a <code class="literal">Collection<Date></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"><</code><code class="n">Object</code><code class="o">></code> <code class="n">lo</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="n">ld</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">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"><</code><code class="n">Number</code><code class="o">></code> <code class="n">cn</code><code class="o">;</code>
<code class="n">List</code><code class="o"><</code><code class="n">Integer</code><code class="o">></code> <code class="n">li</code> <code class="o">=</code> <code class="k">new</code> <code class="n">ArrayList</code><code class="o"><</code><code class="n">Integer</code><code class="o">>();</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<Date> a List<Object>?"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-8-SECT-4.1"/>Why Isn’t a List<Date> a List<Object>?</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<Date></code> to a <code class="literal">List<Object></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>