UNPKG

epubjs

Version:

Render ePub documents in the browser, across many devices

176 lines (171 loc) 28.9 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>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">&lt;</code> <code class="n">T</code> <code class="o">&gt;</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">&lt;</code><code class="n">Date</code><code class="o">&gt;</code> <code class="n">sortDates</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">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">&lt;?&gt;</code> <code class="n">reverse</code><code class="o">(</code> <code class="n">List</code><code class="o">&lt;?&gt;</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">&lt;&gt;</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">&lt;</code><code class="n">T</code><code class="o">&gt;</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">&lt;</code><code class="n">T</code> <code class="kd">extends</code> <code class="n">Entry</code> <code class="o">&amp;</code> <code class="n">Cacheable</code> <code class="o">&gt;</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">&lt;</code><code class="n">T</code> <code class="kd">extends</code> <code class="n">Number</code><code class="o">&gt;</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">&lt;</code><code class="n">T</code><code class="o">&gt;</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 &amp; </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">&lt;</code><code class="n">T</code><code class="o">&gt;</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">&lt;</code><code class="n">T</code><code class="o">&gt;</code> <code class="n">Trap</code><code class="o">&lt;</code><code class="n">T</code><code class="o">&gt;</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">&lt;</code><code class="n">T</code><code class="o">&gt;();</code> <code class="o">}</code> <code class="c1">// usage</code> <code class="n">Trap</code><code class="o">&lt;</code><code class="n">Mouse</code><code class="o">&gt;</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">&lt;</code><code class="n">Bear</code><code class="o">&gt;</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">.&lt;</code><code class="n">Integer</code><code class="o">&gt;</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">.&lt;</code><code class="n">String</code><code class="o">&gt;</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">.&lt;</code><code class="n">String</code><code class="o">&gt;</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">&lt;</code><code class="n">T</code><code class="o">&gt;</code> <code class="n">Set</code><code class="o">&lt;</code><code class="n">T</code><code class="o">&gt;</code> <code class="n">listToSet</code><code class="o">(</code> <code class="n">List</code><code class="o">&lt;</code><code class="n">T</code><code class="o">&gt;</code> <code class="n">list</code> <code class="o">)</code> <code class="o">{</code> <code class="n">Set</code><code class="o">&lt;</code><code class="n">T</code><code class="o">&gt;</code> <code class="n">set</code> <code class="o">=</code> <code class="k">new</code> <code class="n">HashSet</code><code class="o">&lt;</code><code class="n">T</code><code class="o">&gt;();</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">&lt;?&gt;</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> <code class="n">Set</code><code class="o">&lt;?&gt;</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">&lt;?&gt;</code> <code class="n">swap</code><code class="o">(</code> <code class="n">List</code><code class="o">&lt;?&gt;</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">&lt;</code><code class="n">T</code><code class="o">&gt;</code> <code class="n">List</code><code class="o">&lt;</code><code class="n">T</code><code class="o">&gt;</code> <code class="n">swapGeneric</code><code class="o">(</code> <code class="n">List</code><code class="o">&lt;</code><code class="n">T</code><code class="o">&gt;</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">&lt;?&gt;</code> <code class="n">swap</code><code class="o">(</code> <code class="n">List</code><code class="o">&lt;?&gt;</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>