epubjs
Version:
Render ePub documents in the browser, across many devices
176 lines (171 loc) • 28.9 kB
HTML
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Generic Methods</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="Generic Methods"><div class="titlepage"><div><div><h1 class="title"><a id="learnjava3-CHP-8-SECT-9"/>Generic Methods</h1></div></div></div><p>Thus far in this chapter, we’ve talked about generic types and the
implementation of generic classes. Now, we’re going to look at a different
kind of generic animal: <span class="emphasis"><em>generic methods</em></span>. Generic
methods essentially do for individual methods what type parameters do for
generic classes. But as we’ll see, generic methods are smarter and can
figure out their parameter types from their usage context without having
to be explicitly parameterized. (In reality, of course, it is the compiler
that does this.) Generic methods can appear in any class (not just generic
classes) and are very useful for a wide variety of applications.</p><p>First, let’s quickly review the way that we’ve seen regular methods
interact with generic types. We’ve seen that generic classes can contain
methods that use type variables in their arguments and return types in
order to adapt themselves to the parameterization of the class. We’ve also
mentioned that generic types themselves can be used in most of the places
that any other type can be used. So methods of generic or nongeneric
classes can use generic types as argument and return types as well. Here
are examples of those usages:</p><a id="I_8_tt453"/><pre class="programlisting"> <code class="c1">// Not generic methods</code>
<code class="kd">class</code> <code class="nc">GenericClass</code><code class="o"><</code> <code class="n">T</code> <code class="o">></code> <code class="o">{</code>
<code class="c1">// method using generic class parameter type</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="n">T</code> <code class="nf">cache</code><code class="o">(</code> <code class="n">T</code> <code class="n">entry</code> <code class="o">)</code> <code class="o">{</code> <code class="o">...</code> <code class="o">}</code>
<code class="o">}</code>
<code class="kd">class</code> <code class="nc">RegularClass</code> <code class="o">{</code>
<code class="c1">// method using concrete generic type</code>
<code class="kd">public</code> <code class="n">List</code><code class="o"><</code><code class="n">Date</code><code class="o">></code> <code class="n">sortDates</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">dates</code> <code class="o">)</code> <code class="o">{</code> <code class="o">...</code> <code class="o">}</code>
<code class="c1">// method using wildcard generic type</code>
<code class="kd">public</code> <code class="n">List</code><code class="o"><?></code> <code class="n">reverse</code><code class="o">(</code> <code class="n">List</code><code class="o"><?></code> <code class="n">dates</code> <code class="o">)</code> <code class="o">{</code> <code class="o">...</code> <code class="o">}</code>
<code class="o">}</code></pre><p>The <code class="literal">cache()</code> method in <code class="literal">GenericClass</code> accepts an argument of the
parameter type <code class="literal">T</code> and also returns a
value of type <code class="literal">T</code>. The <code class="literal">sortDates()</code> method, which appears in the
nongeneric example class, works with a concrete generic type, and the
<code class="literal">reverse()</code> method works with a wildcard
instantiation of a generic type. These are examples of methods that work
with generics, but they are not true generic methods.</p><div class="sect2" title="Generic Methods Introduced"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-8-SECT-9.1"/>Generic Methods Introduced</h2></div></div></div><p><a id="idx10412" class="indexterm"/> <a id="idx10427" class="indexterm"/>Like generic classes, generic methods have a parameter
type declaration using the <code class="literal"><></code>
syntax. This syntax appears before the return type of the method:</p><a id="I_8_tt454"/><pre class="programlisting"> <code class="c1">// generic method</code>
<code class="o"><</code><code class="n">T</code><code class="o">></code> <code class="n">T</code> <code class="n">cache</code><code class="o">(</code> <code class="n">T</code> <code class="n">entry</code> <code class="o">)</code> <code class="o">{</code> <code class="o">...</code> <code class="o">}</code></pre><p>This <code class="literal">cache()</code> method looks very
much like our earlier example, except that it has its own parameter type
declaration that defines the type variable <a id="I_indexterm8_id712161" class="indexterm"/><code class="literal">T</code>. This method is a
generic method and can appear in either a generic or nongeneric class.
The scope of <code class="literal">T</code> is limited to the
method <code class="literal">cache()</code> and hides any
definition of <code class="literal">T</code> in any enclosing
generic class. As with generic classes, the type <code class="literal">T</code> can have bounds:</p><a id="I_8_tt455"/><pre class="programlisting"> <code class="o"><</code><code class="n">T</code> <code class="kd">extends</code> <code class="n">Entry</code> <code class="o">&</code> <code class="n">Cacheable</code> <code class="o">></code> <code class="n">T</code> <code class="n">cache</code><code class="o">(</code> <code class="n">T</code> <code class="n">entry</code> <code class="o">)</code> <code class="o">{</code> <code class="o">...</code> <code class="o">}</code></pre><p>Unlike a generic class, it does not have to be instantiated with a
specific parameter type for <code class="literal">T</code> before
it is used. Instead, it <span class="emphasis"><em>infers</em></span> the parameter type
<code class="literal">T</code> from the type of its argument,
<code class="literal">entry</code>. For example:</p><a id="I_8_tt456"/><pre class="programlisting"> <code class="n">BlogEntry</code> <code class="n">newBlogEntry</code> <code class="o">=</code> <code class="o">...;</code>
<code class="n">NewspaperEntry</code> <code class="n">newNewspaperEntry</code> <code class="o">=</code> <code class="o">...;</code>
<code class="n">BlogEntry</code> <code class="n">oldEntry</code> <code class="o">=</code> <code class="n">cache</code><code class="o">(</code> <code class="n">newBlogEntry</code> <code class="o">);</code>
<code class="n">NewspaperEntry</code> <code class="n">old</code> <code class="o">=</code> <code class="n">cache</code><code class="o">(</code> <code class="n">newNewspaperEntry</code> <code class="o">);</code></pre><p>Here, our generic method <code class="literal">cache()</code> inferred the type <code class="literal">BlogEntry</code> (which we’ll presume for the sake of
the example is a type of <code class="literal">Entry</code> and
<code class="literal">Cacheable</code>). <code class="literal">BlogEntry</code> became the type <code class="literal">T</code> of the return type and may have been used
elsewhere internally by the method. In the next case, the <code class="literal">cache()</code> method was used on a different type of
<code class="literal">Entry</code> and was able to return the new
type in exactly the same way. That’s what’s powerful about generic
methods: the ability to infer a parameter type from their usage context.
We’ll go into detail about that next.</p><p>Another difference with generic class components is that generic
methods may be static:</p><a id="I_8_tt457"/><pre class="programlisting"> <code class="kd">class</code> <code class="nc">MathUtils</code> <code class="o">{</code>
<code class="kd">public</code> <code class="kd">static</code> <code class="o"><</code><code class="n">T</code> <code class="kd">extends</code> <code class="n">Number</code><code class="o">></code> <code class="n">T</code> <code class="n">max</code><code class="o">(</code> <code class="n">T</code> <code class="n">x</code><code class="o">,</code> <code class="n">T</code> <code class="n">y</code> <code class="o">)</code> <code class="o">{</code> <code class="o">...</code> <code class="o">}</code>
<code class="o">}</code></pre><p>Constructors for classes are essentially methods, too, and follow
the same rules as generic methods, minus the return type.<a id="I_indexterm8_id712306" class="indexterm"/><a id="I_indexterm8_id712313" class="indexterm"/></p></div><div class="sect2" title="Type Inference from Arguments"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-8-SECT-9.2"/>Type Inference from Arguments</h2></div></div></div><p><a id="idx10413" class="indexterm"/> <a id="idx10428" class="indexterm"/>In the previous section, we saw a method infer its type
from an argument:</p><a id="I_8_tt458"/><pre class="programlisting"> <code class="o"><</code><code class="n">T</code><code class="o">></code> <code class="n">T</code> <code class="n">cache</code><code class="o">(</code> <code class="n">T</code> <code class="n">entry</code> <code class="o">)</code> <code class="o">{</code> <code class="o">...</code> <code class="o">}</code></pre><p>But what if there is more than one argument? We saw just that
situation in our last snippet, the static generic method <code class="literal">max( x, y )</code>. All looks well when we give it
two identical types:</p><a id="I_8_tt459"/><pre class="programlisting"> <code class="n">Integer</code> <code class="n">max</code> <code class="o">=</code> <code class="n">MathUtils</code><code class="o">.</code><code class="na">max</code><code class="o">(</code> <code class="k">new</code> <code class="n">Integer</code><code class="o">(</code><code class="mi">1</code><code class="o">),</code> <code class="k">new</code> <code class="n">Integer</code><code class="o">(</code> <code class="mi">2</code> <code class="o">)</code> <code class="o">)</code> <code class="o">;</code></pre><p>But what does it make of the arguments in this invocation?</p><a id="I_8_tt460"/><pre class="programlisting"> <code class="n">MathUtils</code><code class="o">.</code><code class="na">max</code><code class="o">(</code> <code class="k">new</code> <code class="n">Integer</code><code class="o">(</code><code class="mi">1</code><code class="o">),</code> <code class="k">new</code> <code class="n">Float</code><code class="o">(</code> <code class="mi">2</code> <code class="o">)</code> <code class="o">)</code> <code class="o">;</code></pre><p><a id="I_indexterm8_id712397" class="indexterm"/> <a id="I_indexterm8_id712404" class="indexterm"/>In this case, the Java compiler does something really
smart. It climbs up the argument type parent classes, looking for the
<span class="emphasis"><em>nearest common supertype</em></span>. Java also identifies the
<span class="emphasis"><em>nearest common interfaces</em></span> implemented by both of
the types. It identifies that both the <code class="literal">Integer</code> and the <code class="literal">Float</code> types are subtypes of the <code class="literal">Number</code> type. It also recognizes that each of
these implements (a certain generic instantiation of) the <code class="literal">Comparable</code> interface. Java then effectively
makes this combination of types the parameter type of <code class="literal">T</code> for this method invocation. The resulting
type is, to use the syntax of bounds, <code class="literal">Number & </code><code class="literal">Comparable</code>. What this means to us is that
the result type <code class="literal">T</code> is assignable to
anything matching that particular combination of types.</p><a id="I_8_tt461"/><pre class="programlisting"> <code class="n">Number</code> <code class="n">max</code> <code class="o">=</code> <code class="n">MathUtils</code><code class="o">.</code><code class="na">max</code><code class="o">(</code> <code class="k">new</code> <code class="n">Integer</code><code class="o">(</code><code class="mi">1</code><code class="o">),</code> <code class="k">new</code> <code class="n">Float</code><code class="o">(</code> <code class="mi">2</code> <code class="o">)</code> <code class="o">);</code>
<code class="n">Comparable</code> <code class="n">max</code> <code class="o">=</code> <code class="n">MathUtils</code><code class="o">.</code><code class="na">max</code><code class="o">(</code> <code class="k">new</code> <code class="n">Integer</code><code class="o">(</code><code class="mi">1</code><code class="o">),</code> <code class="k">new</code> <code class="n">Float</code><code class="o">(</code> <code class="mi">2</code> <code class="o">)</code> <code class="o">);</code></pre><p>In English, this statement says that we can work with our <code class="literal">Integer</code> and our <code class="literal">Float</code> at the same time only if we think of
them as <code class="literal">Number</code>s or <code class="literal">Comparable</code>s, which makes sense. The return
type has become a new type, which is effectively a <code class="literal">Number</code> that also implements the <code class="literal">Comparable</code> interface.</p><p>This same inference logic works with any number of arguments. But
to be useful, the arguments really have to share some important common
supertype or interface. If they don’t have anything in common, the
result will be their de facto common ancestor, the <code class="literal">Object</code> type. For example, the nearest common
supertype of a <code class="literal">String</code> and a <code class="literal">List</code> is <code class="literal">Object</code> along with the <code class="literal">Serializeable</code> interface. There’s not much a
method could do with a type lacking real bounds anyway.<a id="I_indexterm8_id712550" class="indexterm"/><a id="I_indexterm8_id712557" class="indexterm"/></p></div><div class="sect2" title="Type Inference from Assignment Context"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-8-SECT-9.3"/>Type Inference from Assignment Context</h2></div></div></div><p><a id="idx10414" class="indexterm"/> <a id="idx10429" class="indexterm"/>We’ve seen a generic method infer its parameter type from
its argument types. But what if the type variable isn’t used in any of
the arguments or the method has no arguments? Suppose the method only
has a parametric return type:</p><a id="I_8_tt462"/><pre class="programlisting"> <code class="o"><</code><code class="n">T</code><code class="o">></code> <code class="n">T</code> <code class="n">foo</code><code class="o">()</code> <code class="o">{</code> <code class="o">...</code> <code class="o">}</code></pre><p>You might guess that this is an error because the compiler would
appear to have no way of determining what type we want. But it’s not!
The Java compiler is smart enough to look at the context in which the
method is called. Specifically, if the result of the method is assigned
to a variable, the compiler tries to make the type of that variable the
parameter type. Here’s an example. We’ll make a factory for our <code class="literal">Trap</code> objects:</p><a id="I_8_tt463"/><pre class="programlisting"> <code class="o"><</code><code class="n">T</code><code class="o">></code> <code class="n">Trap</code><code class="o"><</code><code class="n">T</code><code class="o">></code> <code class="n">makeTrap</code><code class="o">()</code> <code class="o">{</code> <code class="k">return</code> <code class="k">new</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="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="n">makeTrap</code><code class="o">();</code>
<code class="n">Trap</code><code class="o"><</code><code class="n">Bear</code><code class="o">></code> <code class="n">bearTrap</code> <code class="o">=</code> <code class="n">makeTrap</code><code class="o">();</code></pre><p>The compiler has, as if by magic, determined what kind of
instantiation of <code class="literal">Trap</code> we want based
on the assignment context.</p><p>Before you get too excited about the possibilities, there’s not
much you can do with a plain type parameter in the body of that method.
For example, we can’t create instances of any particular concrete type
<code class="literal">T</code>, so this limits the usefulness of
factories. About all we can do is the sort of thing shown here, where we
create instances of generics parameterized correctly for the
context.</p><p>Furthermore, the inference only works on assignment to a variable.
Java does not try to guess the parameter type based on the context if
the method call is used in other ways, such as to produce an argument to
a method or as the value of a return statement from a method. In those
cases, the inferred type defaults to type <code class="literal">Object</code>. (See the section <a class="xref" href="ch08s09.html#learnjava3-CHP-8-SECT-9.4" title="Explicit Type Invocation"/> for a
solution.)<a id="I_indexterm8_id712679" class="indexterm"/><a id="I_indexterm8_id712686" class="indexterm"/></p></div><div class="sect2" title="Explicit Type Invocation"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-8-SECT-9.4"/>Explicit Type Invocation</h2></div></div></div><p><a id="I_indexterm8_id712700" class="indexterm"/> <a id="I_indexterm8_id712711" class="indexterm"/>Although it should not be needed often, a syntax does
exist for invoking a generic method with specific parameter types. The
syntax is a bit awkward and involves a class or instance object prefix,
followed by the familiar angle bracket type list, placed before the
actual method invocation. Here are some examples:</p><a id="I_8_tt464"/><pre class="programlisting"> <code class="n">Integer</code> <code class="n">i</code> <code class="o">=</code> <code class="n">MathUtilities</code><code class="o">.<</code><code class="n">Integer</code><code class="o">></code><code class="n">max</code><code class="o">(</code> <code class="mi">42</code><code class="o">,</code> <code class="mi">42</code> <code class="o">);</code>
<code class="n">String</code> <code class="n">s</code> <code class="o">=</code> <code class="n">fooObject</code><code class="o">.<</code><code class="n">String</code><code class="o">></code><code class="n">foo</code><code class="o">(</code> <code class="s">"foo"</code> <code class="o">);</code>
<code class="n">String</code> <code class="n">s</code> <code class="o">=</code> <code class="k">this</code><code class="o">.<</code><code class="n">String</code><code class="o">></code><code class="n">foo</code><code class="o">(</code> <code class="s">"foo"</code> <code class="o">);</code></pre><p>The prefix must be a class or object instance containing the
method. One situation where you’d need to use explicit type invocation
is if you are calling a generic method that infers its type from the
assignment context, but you are not assigning the value to a variable
directly. For example, if you wanted to pass the result of our <code class="literal">makeTrap()</code> method as a parameter to another
method, it would otherwise default to <code class="literal">Object</code>.</p></div><div class="sect2" title="Wildcard Capture"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-8-SECT-9.5"/>Wildcard Capture</h2></div></div></div><p><a id="idx10415" class="indexterm"/> <a id="idx10430" class="indexterm"/>Generic methods can do one more trick for us involving
taming wildcard instantiations of generic types. The term
<span class="emphasis"><em>wildcard capture</em></span> refers to the fact that generic
methods can work with arguments whose type is a wildcard instantiation
of a type, just as if the type were known:</p><a id="I_8_tt465"/><pre class="programlisting"> <code class="o"><</code><code class="n">T</code><code class="o">></code> <code class="n">Set</code><code class="o"><</code><code class="n">T</code><code class="o">></code> <code class="n">listToSet</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="n">Set</code><code class="o"><</code><code class="n">T</code><code class="o">></code> <code class="n">set</code> <code class="o">=</code> <code class="k">new</code> <code class="n">HashSet</code><code class="o"><</code><code class="n">T</code><code class="o">>();</code>
<code class="n">set</code><code class="o">.</code><code class="na">addAll</code><code class="o">(</code> <code class="n">list</code> <code class="o">);</code>
<code class="k">return</code> <code class="n">set</code><code class="o">;</code>
<code class="o">}</code>
<code class="c1">// usage</code>
<code class="n">List</code><code class="o"><?></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>
<code class="n">Set</code><code class="o"><?></code> <code class="n">set</code> <code class="o">=</code> <code class="n">listToSet</code><code class="o">(</code> <code class="n">list</code> <code class="o">);</code></pre><p>The result of these examples is that we converted an unknown
instantiation of <code class="literal">List</code> to an unknown
instantiation of <code class="literal">Set</code>. The type
variable <code class="literal">T</code> represents the actual type
of the argument, <code class="literal">list</code>, for purposes
of the method body. The wildcard instantiation must match any bounds of
the method parameter type. But because we can work with the type
variable only through its bounds types, the compiler is free to refer to
it by this new name, <code class="literal">T</code>, as if it were
a known type. That may not seem very interesting, but it is useful
because it allows methods that accept wildcard instantiations of types
to delegate their work to other generic methods.</p><p>Another way to look at this is that generic methods are a more
powerful alternative to methods using wildcard instantiations of types.
We’ll do a little comparison next.<a id="I_indexterm8_id712848" class="indexterm"/><a id="I_indexterm8_id712855" class="indexterm"/></p></div><div class="sect2" title="Wildcard Types Versus Generic Methods"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-8-SECT-9.6"/>Wildcard Types Versus Generic Methods</h2></div></div></div><p><a id="I_indexterm8_id712869" class="indexterm"/> <a id="I_indexterm8_id712880" class="indexterm"/>You’ll recall that trying to work with an object through a
wildcard instantiation of its generic type limits us to “reading” the
object. We cannot “write” types to the object because its parameter type
is unknown. In contrast, because generic methods can infer or “capture”
an actual type for their arguments, they allow us to do a lot more with
broad ranges of types than we could with wildcard instantiations
alone.</p><p>For example, suppose we wanted to write a utility method that
swaps the first two elements of a list. Using wildcards, we’d like to
write something like this:</p><a id="I_8_tt466"/><pre class="programlisting"> <code class="c1">// Bad implementation</code>
<code class="n">List</code><code class="o"><?></code> <code class="n">swap</code><code class="o">(</code> <code class="n">List</code><code class="o"><?></code> <code class="n">list</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">Object</code> <code class="n">tmp</code> <code class="o">=</code> <code class="n">list</code><code class="o">.</code><code class="na">get</code><code class="o">(</code><code class="mi">0</code><code class="o">);</code>
<code class="n">list</code><code class="o">.</code><code class="na">set</code><code class="o">(</code> <code class="mi">0</code><code class="o">,</code> <code class="n">list</code><code class="o">.</code><code class="na">get</code><code class="o">(</code><code class="mi">1</code><code class="o">)</code> <code class="o">);</code> <code class="c1">// error, can't write</code>
<code class="n">list</code><code class="o">.</code><code class="na">set</code><code class="o">(</code> <code class="mi">1</code><code class="o">,</code> <code class="n">tmp</code> <code class="o">);</code> <code class="c1">// error, can't write</code>
<code class="k">return</code> <code class="n">list</code><code class="o">;</code>
<code class="o">}</code></pre><p>But we are not allowed to call the <code class="literal">set()</code> method of our list because we don’t know
what type it actually holds. We are really stuck and there isn’t much we
can do. But the corresponding generic method gives us a real type to
hang our hat:</p><a id="I_8_tt467"/><pre class="programlisting"> <code class="o"><</code><code class="n">T</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">swapGeneric</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="n">T</code> <code class="n">tmp</code> <code class="o">=</code> <code class="n">list</code><code class="o">.</code><code class="na">get</code><code class="o">(</code> <code class="mi">0</code> <code class="o">);</code>
<code class="n">list</code><code class="o">.</code><code class="na">set</code><code class="o">(</code> <code class="mi">0</code><code class="o">,</code> <code class="n">list</code><code class="o">.</code><code class="na">get</code><code class="o">(</code><code class="mi">1</code><code class="o">)</code> <code class="o">);</code>
<code class="n">list</code><code class="o">.</code><code class="na">set</code><code class="o">(</code> <code class="mi">1</code><code class="o">,</code> <code class="n">tmp</code> <code class="o">);</code>
<code class="k">return</code> <code class="n">list</code><code class="o">;</code>
<code class="o">}</code></pre><p>Here, we are able to declare a variable of the correct (inferred)
type and write using the <code class="literal">set()</code>
methods appropriately. It would seem that generic methods are the only
way to go here. But there is a third path. Wildcard capture, as
described in the previous section, allows us to delegate our wildcard
version of the method to our actual generic method and use it as if the
type were inferred, even though it’s open-ended:</p><a id="I_8_tt468"/><pre class="programlisting"> <code class="n">List</code><code class="o"><?></code> <code class="n">swap</code><code class="o">(</code> <code class="n">List</code><code class="o"><?></code> <code class="n">list</code> <code class="o">)</code> <code class="o">{</code>
<code class="k">return</code> <code class="nf">swapGeneric</code><code class="o">(</code> <code class="n">list</code> <code class="o">);</code> <code class="c1">// delegate to generic form</code>
<code class="o">}</code></pre><p>Here, we delegated to the generic version.</p></div></div></body></html>