epubjs
Version:
Render ePub documents in the browser, across many devices
165 lines (158 loc) • 23.2 kB
HTML
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Writing Generic Classes</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="Writing Generic Classes"><div class="titlepage"><div><div><h1 class="title"><a id="learnjava3-CHP-8-SECT-6"/>Writing Generic Classes</h1></div></div></div><p>Now that we have (at least some of) the “end user” view of generics,
let’s try writing a few classes ourselves. In this section, we’ll talk
about how type variables are used in the definition of generic classes,
where they may appear, and some of their limitations. We’ll also talk
about subclassing generic types.</p><div class="sect2" title="The Type Variable"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-8-SECT-6.1"/>The Type Variable</h2></div></div></div><p><a id="I_indexterm8_id709162" class="indexterm"/> <a id="I_indexterm8_id709168" class="indexterm"/> <a id="idx10395" class="indexterm"/> <a id="idx10411" class="indexterm"/>We’ve already seen the basics of how type variables are
used in the declaration of a generic class. One or more type variables
are declared in the angle bracket (<>) type declaration and used
throughout the body and instance methods of the class. For example:</p><a id="I_8_tt421"/><pre class="programlisting"> <code class="kd">class</code> <code class="nc">Mouse</code> <code class="o">{</code> <code class="o">}</code>
<code class="kd">class</code> <code class="nc">Bear</code> <code class="o">{</code> <code class="o">}</code>
<code class="kd">class</code> <code class="nc">Trap</code><code class="o"><</code> <code class="n">T</code> <code class="o">></code>
<code class="o">{</code>
<code class="n">T</code> <code class="n">trapped</code><code class="o">;</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">snare</code><code class="o">(</code> <code class="n">T</code> <code class="n">trapped</code> <code class="o">)</code> <code class="o">{</code> <code class="k">this</code><code class="o">.</code><code class="na">trapped</code> <code class="o">=</code> <code class="n">trapped</code><code class="o">;</code> <code class="o">}</code>
<code class="kd">public</code> <code class="n">T</code> <code class="nf">release</code><code class="o">()</code> <code class="o">{</code> <code class="k">return</code> <code class="n">trapped</code><code class="o">;</code> <code class="o">}</code>
<code class="o">}</code>
<code class="c1">// usage</code>
<code class="n">Trap</code><code class="o"><</code><code class="n">Mouse</code><code class="o">></code> <code class="n">mouseTrap</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Trap</code><code class="o"><</code><code class="n">Mouse</code><code class="o">>();</code>
<code class="n">mouseTrap</code><code class="o">.</code><code class="na">snare</code><code class="o">(</code> <code class="k">new</code> <code class="n">Mouse</code><code class="o">()</code> <code class="o">);</code>
<code class="n">Mouse</code> <code class="n">mouse</code> <code class="o">=</code> <code class="n">mouseTrap</code><code class="o">.</code><code class="na">release</code><code class="o">();</code></pre><p>Here, we created a generic <code class="literal">Trap</code>
class that can hold any type of object. We used the type variable
<a id="I_indexterm8_id709234" class="indexterm"/><code class="literal">T</code> to declare an
instance variable of the parameter type as well as in the argument type
and return type of the two methods.</p><p>The scope of the type variable is the instance portion of the
class, including methods and any instance initializer blocks. The static
portion of the class is not affected by the generic parameterization,
and type variables are not visible in static methods or static
initializers. As you might guess, just as all instantiations of the
generic type have only one actual class (the raw type), they have only
one, shared static context as well. You cannot even invoke a static
method through a parameterized type. You must use the raw type or an
instance of the object.</p><p>The type variable can also be used in the type instantiation of
other generic types used by the class. For example, if we wanted our
<code class="literal">Trap</code> to hold more than one animal, we
could create a <code class="literal">List</code> for them within
our class by referencing the parameter type like so:</p><a id="I_8_tt422"/><pre class="programlisting"> <code class="n">List</code><code class="o"><</code><code class="n">T</code><code class="o">></code> <code class="n">trappedList</code> <code class="o">=</code> <code class="k">new</code> <code class="n">ArrayList</code><code class="o"><</code><code class="n">T</code><code class="o">>();</code></pre><p>Just to cover all the bases, we should mention that instantiations
of generic types on the type variable act just like any other type and
can serve in all the places that other instantiations of a type can. For
example, a method in our class can take a <code class="literal">List<T></code> as an argument:</p><a id="I_8_tt423"/><pre class="programlisting"> <code class="kd">public</code> <code class="kt">void</code> <code class="nf">trapAll</code><code class="o">(</code> <code class="n">List</code><code class="o"><</code><code class="n">T</code><code class="o">></code> <code class="n">list</code> <code class="o">)</code> <code class="o">{</code> <code class="o">...</code> <code class="o">}</code></pre><p>The effective type of the <code class="literal">trapAll()</code> method in a <code class="literal">Trap<Mouse></code> is then simply:</p><a id="I_8_tt424"/><pre class="programlisting"> <code class="n">trapAll</code><code class="o">(</code> <code class="n">List</code><code class="o"><</code><code class="n">Mouse</code><code class="o">></code> <code class="n">list</code> <code class="o">)</code> <code class="o">{</code> <code class="o">...</code> <code class="o">}</code></pre><p>We should note that this is <span class="emphasis"><em>not</em></span> what we mean
by the term <span class="emphasis"><em>generic method</em></span>. This is just a regular
Java method that happens to take a generic type as an argument. We’ll
talk about real generic methods, which can <span class="emphasis"><em>infer</em></span>
their types from arguments, and assignment contexts later in this
chapter. A type variable can also be used to parameterize a generic
parent class, as we’ll see in the next section.<a id="I_indexterm8_id709338" class="indexterm"/><a id="I_indexterm8_id709345" class="indexterm"/></p></div><div class="sect2" title="Subclassing Generics"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-8-SECT-6.2"/>Subclassing Generics</h2></div></div></div><p><a id="idx10394" class="indexterm"/> <a id="idx10410" class="indexterm"/> <a id="idx10436" class="indexterm"/>Generic types can be subclassed just like any other class
by either generic or nongeneric child classes. A nongeneric subclass
must extend a particular instantiation of the parent type, filling in
the required parameters to make it concrete:</p><a id="I_8_tt425"/><pre class="programlisting"> <code class="kd">class</code> <code class="nc">DateList</code> <code class="kd">extends</code> <code class="n">ArrayList</code><code class="o"><</code><code class="n">Date</code><code class="o">></code> <code class="o">{</code> <code class="o">}</code>
<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">dateList</code><code class="o">.</code><code class="na">add</code><code class="o">(</code> <code class="k">new</code> <code class="n">Date</code><code class="o">()</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="n">dateList</code><code class="o">;</code></pre><p>Here, we have created a nongeneric subclass, <code class="literal">DateList</code>, of the concrete generic
instantiation <code class="literal">ArrayList<Date></code>.
The <code class="literal">DateList</code> is a type of <code class="literal">ArrayList<Date></code> and inherits the
particular instantiation of all of the methods, just as it would from
any other parent. We can even assign it back to the parent type if we
wish, as shown in this example.</p><p>A generic subtype of a generic class may extend either a concrete
instantiation of the class, as in the previous example, or it may share
a type variable that it “passes up” to the parent upon
instantiation:</p><a id="I_8_tt426"/><pre class="programlisting"> <code class="kd">class</code> <code class="nc">AdjustableTrap</code><code class="o"><</code> <code class="n">T</code> <code class="o">></code> <code class="kd">extends</code> <code class="n">Trap</code><code class="o"><</code> <code class="n">T</code> <code class="o">></code> <code class="o">{</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">setSize</code><code class="o">(</code> <code class="kt">int</code> <code class="n">i</code> <code class="o">)</code> <code class="o">{</code> <code class="o">...</code> <code class="o">}</code>
<code class="o">}</code></pre><p>Here, the type variable <code class="literal">T</code> used
to instantiate the <code class="literal">AdjustableTrap</code>
class is passed along to instantiate the base class, <code class="literal">Trap</code>. When the user instantiates the <code class="literal">AdjustableTrap</code> on a particular parameter type,
the parent class is instantiated on that type as well.<a id="I_indexterm8_id709482" class="indexterm"/><a id="I_indexterm8_id709489" class="indexterm"/><a id="I_indexterm8_id709496" class="indexterm"/></p></div><div class="sect2" title="Exceptions and Generics"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-8-SECT-6.3"/>Exceptions and Generics</h2></div></div></div><p><a id="idx10392" class="indexterm"/> <a id="idx10400" class="indexterm"/> <a id="idx10408" class="indexterm"/>Types appear in the body of classes in another place—the
<code class="literal">throws</code> clauses of methods. We can use
type variables to define the type of exceptions thrown by methods, but
to do so we need to introduce the concept of bounds. We cover bounds
more in the next section. In this case, the usage is very simple. We
just need to ensure that the type variable we want to use as our
exception type is actually a type of <code class="literal">Throwable</code>. We can do that by adding an
<code class="literal">extends</code> clause to the declaration of
our type variable, like this:</p><a id="I_8_tt427"/><pre class="programlisting"> <code class="o"><</code> <code class="n">T</code> <code class="kd">extends</code> <code class="n">Throwable</code> <code class="o">></code></pre><p>Here is an example class, parameterized on a type that must be a
kind of <code class="literal">Throwable</code>. Its <code class="literal">test()</code> method accepts an instance of that kind
of object and throws it as a checked exception:</p><a id="I_8_tt428"/><pre class="programlisting"> <code class="n">ExceptionTester</code><code class="o"><</code> <code class="n">T</code> <code class="kd">extends</code> <code class="n">Throwable</code> <code class="o">></code> <code class="o">{</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">test</code><code class="o">(</code> <code class="n">T</code> <code class="n">exception</code> <code class="o">)</code> <code class="kd">throws</code> <code class="n">T</code> <code class="o">{</code><code class="c1">// throw type is generic param</code>
<code class="k">throw</code> <code class="n">exception</code><code class="o">;</code>
<code class="o">}</code>
<code class="o">}</code>
<code class="k">try</code> <code class="o">{</code>
<code class="k">new</code> <code class="n">ExceptionTester</code><code class="o"><</code><code class="n">ClassNotFoundException</code><code class="o">>().</code><code class="na">test</code><code class="o">(</code>
<code class="k">new</code> <code class="nf">ClassNotFoundException</code><code class="o">()</code> <code class="o">);</code>
<code class="o">}</code> <code class="k">catch</code> <code class="o">(</code> <code class="n">ClassNotFoundException</code> <code class="n">e</code> <code class="o">)</code> <code class="o">{</code> <code class="o">...</code> <code class="o">}</code></pre><p>The important part of this example is that the <code class="literal">throws</code> clause of our test method is defined to
throw <code class="literal">T</code>, the generic parameter type
of the class. This means that we can parameterize the type of exceptions
thrown by a class.</p><p>The addition of the bound imposes the restriction that the
parameter type used to instantiate the class <code class="literal">T</code> must be a type of <code class="literal">Throwable</code>. And we referenced the type <code class="literal">T</code> in the <code class="literal">throws</code> clause. So, an <code class="literal">ExceptionTester<ClassNotFoundException></code>
can throw a <code class="literal">ClassNotFoundException</code>
from its <code class="literal">test()</code> method. Note that
this is a checked exception and that fact has not been lost on the
compiler. The compiler enforces the checked exception type that it just
applied.</p><div class="sect3" title="No generic throwables"><div class="titlepage"><div><div><h3 class="title"><a id="learnjava3-CHP-8-SECT-6.3.1"/>No generic throwables</h3></div></div></div><p>We saw that a type variable can be used to specify the type of
<a id="I_indexterm8_id709677" class="indexterm"/><code class="literal">Throwable</code> in the
<code class="literal">throws</code> clause of a method. Perhaps
ironically, however, we cannot use generics to create new types of
exceptions. No generic subtypes of <code class="literal">Throwable</code> are allowed. If you think about
this for a moment, you’ll see that in order to be useful, generic
<code class="literal">Throwable</code>s would require <code class="literal">try/catch</code> blocks that can differentiate
instantiations of <code class="literal">Throwable</code>. And
because (once again) there is no runtime representation of generics,
this isn’t possible with erasure.<a id="I_indexterm8_id709720" class="indexterm"/><a id="I_indexterm8_id709727" class="indexterm"/><a id="I_indexterm8_id709734" class="indexterm"/></p></div></div><div class="sect2" title="Parameter Type Limitations"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-8-SECT-6.4"/>Parameter Type Limitations</h2></div></div></div><p><a id="idx10393" class="indexterm"/> <a id="idx10409" class="indexterm"/> <a id="idx10431" class="indexterm"/>We have seen the parameter types (type variables) of a
generic class used to declare instance variables, method arguments, and
return types as well as “passed along” to parameterize a generic
superclass. One thing that we haven’t talked about is the question of
how or whether we can use the type variable of a generic class to
construct instances of the parameter type or work with objects of the
type in other concrete ways. We deliberately avoided this issue in our
previous “exception tester” example by simply passing our exception
object in as an argument. Could we have done away with this argument?
The answer, unfortunately, is that due to the limitations of erasure,
there really is no parameter type information to work with at runtime.
In this section, we’ll look at this problem and explore a
workaround.</p><p>Because the type variable <code class="literal">T</code> has
faithfully served as our parameter type everywhere else, you might
imagine that we could use it to construct an instance of <code class="literal">T</code> using the <code class="literal">new</code> keyword. But we can’t:</p><a id="I_8_tt429"/><pre class="programlisting"> <code class="n">T</code> <code class="n">element</code> <code class="o">=</code> <code class="k">new</code> <code class="n">T</code><code class="o">();</code> <code class="c1">// Error! Invalid syntax.</code></pre><p>Remember that all type information is erased in the compiled
class. The raw type does not have any way of knowing the type of object
you want to construct at runtime. Nor is there any way to get at the
<code class="literal">Class</code> of the parameter type through
the type variable, for the same reason. So reflection won’t help us here
either. This means that, in general, generics are limited to working
with parameter types in relatively hands-off ways (by reference only).
This is one reason that generics are more useful for containers than in
some other applications. This problem comes up often and there is a
solution, although it’s not quite as elegant as we’d like.</p><div class="sect3" title="Using Class<T>"><div class="titlepage"><div><div><h3 class="title"><a id="learnjava3-CHP-8-SECT-6.4.1"/>Using Class<T></h3></div></div></div><p><a id="idx10391" class="indexterm"/>The only real way to get the type information that we
need at runtime is to have the user explicitly pass in a <code class="literal">Class</code> reference, generally as one of the
arguments to a method. Then we can explicitly refer to the class using
reflection and create instances or do whatever else is necessary. This
may sound like a really bad solution, without much type safety and
placing a big burden on the developer to do the right thing.
Fortunately, we can use a trick of generics to enforce this contract
with the user and make it safe. Again, the basic idea is to have one
of our methods accept the <code class="literal">Class</code> of
the parameter type so that we can use it at runtime. Following our
“exception tester” example:</p><a id="I_8_tt430"/><pre class="programlisting"> <code class="kd">public</code> <code class="kt">void</code> <code class="nf">test</code><code class="o">(</code> <code class="n">Class</code> <code class="n">type</code> <code class="o">)</code> <code class="kd">throws</code> <code class="n">T</code> <code class="o">{</code> <code class="o">...</code> <code class="o">}</code></pre><p>This isn’t much better than it was before. Specifically, it
doesn’t guarantee that the <code class="literal">Class</code>
type passed to the method will match the parameterized type of the
class (used in the <code class="literal">throws</code> clause
here).</p><p>Fortunately, the <code class="literal">Class</code> class
is, itself, now a generic type. Specifically, all instances of the
<code class="literal">Class</code> class created by the Java VM
are instantiated with their own type as a parameter. The class of the
<code class="literal">String</code> type, for example, is now
<code class="literal">Class<String></code>, not just some
arbitrary instance of the raw <code class="literal">Class</code>
type that happens to know about strings.</p><p>This has two ramifications. First, we can specify a particular
instantiation of <code class="literal">Class</code> using the
parameter type in our class. And second, since the <code class="literal">Class</code> class is now generic, all of the
reflective and instance creation methods can be typed properly and no
longer require casts, so we can write our <code class="literal">test()</code> method like this:</p><a id="I_8_tt431"/><pre class="programlisting"> <code class="kd">public</code> <code class="kt">void</code> <code class="nf">test</code><code class="o">(</code> <code class="n">Class</code><code class="o"><</code><code class="n">T</code><code class="o">></code> <code class="n">type</code> <code class="o">)</code> <code class="kd">throws</code> <code class="n">T</code> <code class="o">{</code>
<code class="k">throw</code> <code class="n">type</code><code class="o">.</code><code class="na">newInstance</code><code class="o">();</code>
<code class="o">}</code></pre><p>The only <code class="literal">Class</code> instance that
can be passed to our <code class="literal">test()</code> method
now is <code class="literal">Class<T></code>, the <code class="literal">Class</code> for the parameter type <code class="literal">T</code>, on which we instantiated <code class="literal">ExceptionTester</code>. So, although the user still
has the burden of passing in this seemingly extraneous <code class="literal">Class</code> argument, at least the compiler will
ensure that we do it and do it correctly:</p><a id="I_8_tt432"/><pre class="programlisting"> <code class="n">ExceptionTester</code><code class="o"><</code><code class="n">ArithmeticException</code><code class="o">></code> <code class="n">et</code> <code class="o">=</code>
<code class="k">new</code> <code class="n">ExceptionTester</code><code class="o"><</code><code class="n">ArithmeticException</code><code class="o">>();</code>
<code class="n">et</code><code class="o">.</code><code class="na">test</code><code class="o">(</code> <code class="n">ArithmeticException</code><code class="o">.</code><code class="na">class</code> <code class="o">);</code> <code class="c1">// no other .class will work</code></pre><p>In this code snippet, attempting to pass any other <code class="literal">Class</code> argument to the <code class="literal">test()</code> method generates a compile-time
error.<a id="I_indexterm8_id710033" class="indexterm"/><a id="I_indexterm8_id710040" class="indexterm"/><a id="I_indexterm8_id710047" class="indexterm"/><a id="I_indexterm8_id710054" class="indexterm"/></p></div></div></div></body></html>