blob: 92d2fcc93af97fca943b0529b7ac5915c79ab6b1 [file] [log] [blame]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 2.0.15">
<title>Generics</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<!-- ************* Meta ************* -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<!-- ************* OpenGraph ************-->
<meta name="description" content="The Eclipse N4JS language and its IDE enable high-quality JavaScript development for large Node.js projects.">
<meta property="og:site_name" content="Eclipse N4JS"/>
<meta property="og:title" content="Eclipse N4JS Language and IDE"/>
<meta property="og:url" content="https://numberfour.github.io/n4js"/>
<meta property="og:description" content="The Eclipse N4JS language and its IDE enable high-quality JavaScript development for large Node.js projects."/>
<meta property="og:image" content="../images/n4js.png">
<!-- ************* Favicon ************-->
<link rel="icon" href="../images/favicon.ico" />
<link rel="icon" type="image/png" href="../images/favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="../images/favicon-16x16.png" sizes="16x16" />
<!-- ************* Back-to-top JQuery ************* -->
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.0/jquery-ui.js"></script>
<!-- ************* Prism.js Syntax Highlighter ******-->
<link href="../styles/prism.min.css" rel="stylesheet"/>
<script src="../scripts/prism.js"></script>
<!-- ************* Styles ************* -->
<link rel="stylesheet" type="text/css" href="../styles/n4js-adoc.css">
<!-- ****************** NavBar ****************** -->
<div id="menubar">
<div class="banner">
<a href="../index.html"><img id="logo" src="../images/n4js-logo.png" alt="Eclipse N4JS Language and IDE"></a>
</div>
<ul>
<li><a href="../downloads.html"></i>Download</a></li>
<li><a href="../community.html"></i>Community</a></li>
<li><a href="../userguides/index.html">Documentation</a></li>
</ul>
</div>
<button id="tocbutton">TOC</button>
</head>
<body class="book">
<div id="header">
</div>
<div id="content">
<h1 id="_generics" class="sect0"><a class="link" href="#_generics">Generics</a></h1>
<div class="openblock partintro">
<div class="title">Generics</div>
<div class="content">
Generics in N4JS are a language feature that allows for generic programming. They enable
a function, class, interface, or method to operate on the values of various (possibly unknown)
types while preserving compile-time type safety. If you are familiar with Java&#8217;s generics,
then N4JS&#8217;s generics will look familiar as well. There are some differences, however, which we
shall describe below.
</div>
</div>
<div class="sect1">
<h2 id="_motivation"><a class="link" href="#_motivation">Motivation</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Several language elements may be declared in a generic form; we&#8217;ll start with focusing on classes,
generic methods will be discussed after that.</p>
</div>
<div class="paragraph">
<p>The standard case, of course, is a non-generic class. Take the following class, for example, that
bundles a pair of two strings:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-n4js" data-lang="n4js">export public class PairOfString {
first: string;
second: string;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This implementation is fine as long as all we ever want to store are strings. As experience shows,
someone is soon to come up with the idea of storing two values of another type, such as&#8230;&#8203; numbers!
Let&#8217;s add another class:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-n4js" data-lang="n4js">export public class PairOfNumber {
first: number;
second: number;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Following this pattern of adding more classes for new types to be stored obviously has its limitations.
We would soon end up with a multitude of classes that are basically serving the same purpose, leading to
code duplication, terrible maintainability and many other problems.</p>
</div>
<div class="paragraph">
<p>One solution could be having a class that stores two values of type <code>any</code> (in N4JS,
<code>any</code> is the so-called 'top type', the common supertype of all other types).</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-n4js" data-lang="n4js">export public class PairOfWhatEver {
first: any;
second: any;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Now we&#8217;re worse off than before. We have lost the certainty that within a single pair, both values
will always be of the same type. When reading a value from a pair, we have no clue what its
type might be.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_generic_classes_and_interfaces"><a class="link" href="#_generic_classes_and_interfaces">Generic Classes (and Interfaces)</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>The way to solve our previous conundrum using generics is to introduce a <strong>type variable</strong> for the class
We will then call such a class a <strong>generic class</strong>.
A type variable can then be used within the class declaration just as any other ordinary type.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-n4js" data-lang="n4js">export public class Pair&lt;T&gt; {
first: T;
second: T;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The type variable <code>T</code>, declared after the class name in angle brackets, now represents
the type of the values stored in the <code>Pair</code> and can be used as the type of the two fields.</p>
</div>
<div class="paragraph">
<p>Now, whenever we refer to the class <code>Pair</code>, we will provide a <strong>type argument</strong>, in other words a
type that will be used wherever the type variable <code>T</code> is being used inside the class
declaration.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-n4js" data-lang="n4js">import { Pair } from 'Pair';
let myPair = new Pair&lt;string&gt;();
myPair.first = '1st value';
myPair.second = '2nd value';</code></pre>
</div>
</div>
<div class="paragraph">
<p>By using a type variable, we have not just allowed any given type to be used as value type,
we have also stated that both values, first and second, must always be of the same type. We
have also given the type system a chance to track the types of values stored in a <code>Pair</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-n4js" data-lang="n4js">import { Pair } from 'Pair';
let myPair2 = new Pair&lt;string&gt;();
myPair2.first = '1st value';
myPair2.second = 42; // error: 'int is not a subtype of string.'
console.log(myPair2.first.charAt(2));
// type system will know myPair2.first is of type string</code></pre>
</div>
</div>
<div class="paragraph">
<p>The error in line 3 shows that the type checker will make sure we won&#8217;t put any value of incorrect
type into the pair. The fact that we can access method <code>charAt()</code> (available on strings)
in the last line indicates that when we read a value from the pair, the type system knows its type
and we can use it accordingly.</p>
</div>
<div class="paragraph">
<p>Generic interfaces can be declared in exactly the same way.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_generic_functions_and_methods"><a class="link" href="#_generic_functions_and_methods">Generic Functions (and Methods)</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>With the above, we can now avoid introducing a multitude of classes that are basically serving the
same purpose. It is still not possible, however, to write code that manipulates such pairs regardless of the type of
its values may have. For example, a function for swapping the two values of a pair and
then return the new first value would look like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-n4js" data-lang="n4js">import { PairOfString } from 'PairOfString';
function swapStrings1(pair: PairOfString): string {
let backup = pair.first; // inferred type of 'backup' will be string
pair.first = pair.second;
pair.second = backup;
return pair.first;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The above function would have to be copied for every value type to be supported. Using the generic class
<code>Pair&lt;T&gt;</code> does not help much:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-n4js" data-lang="n4js">import { Pair } from 'Pair';
function swapStrings2(pair: Pair&lt;string&gt;): string {
let backup = pair.first; // inferred type of 'backup' will be string
pair.first = pair.second;
pair.second = backup;
return pair.first;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The solution is not only to make generic the type being manipulated generic (as we have done with class
<code>Pair&lt;T&gt;</code> above) but to make the code performing the manipulation generic:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-n4js" data-lang="n4js">import { Pair } from 'Pair';
function &lt;T&gt; swap(pair: Pair&lt;T&gt;): T {
let backup = pair.first; // inferred type of 'backup' will be T
pair.first = pair.second;
pair.second = backup;
return pair.first;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>We have introduced a type variable for function <code>swap()</code> in much the same way as
we have done for class <code>Pair</code> in the previous section (we then call such a function
a <strong>generic function</strong>). Similarly, we can use the type variable in this function&#8217;s signature
and body.</p>
</div>
<div class="paragraph">
<p>It is possible to state in the declaration of the function <code>swap()</code> above that
it will return something of type <code>T</code> when having obtained a <code>Pair&lt;T&gt;</code> without
even knowing what type that might be. This allows the type system to track the type of values passed
+between functions and methods or put into and taken out of containers and so on.</p>
</div>
<div class="paragraph">
<p><strong>Generic methods</strong> can be declared just as generic functions. There is one caveat, however:
Only if a method introduces its own, new type variables is it called a generic method. If it is
merely using the type variables of its containing class or interface, it&#8217;s an ordinary method.
The following example illustrates the difference:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-n4js" data-lang="n4js">export public class Pair&lt;T&gt; {
public foo(): T { … }
public &lt;S&gt; bar(pair: Pair2&lt;S&gt;): void { … }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The first method <code>foo</code> is a non generic method, while the second one <code>bar</code> is.</p>
</div>
<div class="paragraph">
<p>A very interesting application of static methods is when using in combination with function type arguments:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-n4js" data-lang="n4js">class Pair&lt;T&gt; {
&lt;R&gt; merge(merger: {function(T,T): R}): R {
return merger(this.first, this.second);
}
}
var p = new Pair&lt;string&gt;();
var i = p.merge( (f,s)=&gt; f.length+s.length )</code></pre>
</div>
</div>
<div class="paragraph">
<p>You will notice that N4JS can infer the correct types for the arguments and the return type of the arrow expression. Also the type for <code>i</code> will be automatically computed.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_differences_to_java"><a class="link" href="#_differences_to_java">Differences to Java</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Important differences between generics in Java and N4JS include:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Primitive types can be used as type arguments in N4JS.</p>
</li>
<li>
<p>There are no raw types in N4JS. Whenever a generic class or interface is referenced, a type argument has to be provided - possibly in the form of a wildcard. For generic functions and methods, an
explicit definition of type arguments is optional if the type system can infer the type arguments
from the context.</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
</div>
</div>
<div class="Grid social" style="color:#d5dfea">
<div class="Cell Cell--2-12 m-Cell--withMargin">
<h2>Quick Links</h2>
<ul>
<li><a href="../downloads.html">Download</a></li>
<li><a href="../userguides/index.html">Documentation</a></li>
<li><a href="https://github.com/eclipse/n4js/">Source</a></li>
<li><a href="https://github.com/eclipse/n4js/issues">Issues</a></li>
</ul>
</div>
<div class="Cell Cell--2-12 m-Cell--withMargin">
<br/><br/>
<ul>
<li><a href="https://www.eclipse.org/forums/index.php/f/365/">Forum</a></li>
<li><a href="http://n4js.blogspot.de/">Blog</a></li>
<li><a href="https://dev.eclipse.org/mailman/listinfo/n4js-dev">Mailing List</a></li>
<li><a href="https://projects.eclipse.org/projects/technology.n4js">Eclipse Project Page</a></li>
<li><a href="https://twitter.com/n4jsdev">Tweets by n4jsdev</a></li>
</ul>
</div>
<div class="Cell Cell--2-12 m-Cell--withMargin">
<br/><br/>
<ul>
<li><a href="http://www.eclipse.org/">Eclipse Home</a></li>
<li><a href="http://www.eclipse.org/legal/privacy.php">Privacy Policy</a></li>
<li><a href="http://www.eclipse.org/legal/termsofuse.php">Terms of Use</a></li>
<li><a href="http://www.eclipse.org/legal/copyright.php">Copyright Agent</a></li>
<li><a href="http://www.eclipse.org/legal/">Legal</a></li>
</ul>
</div>
<div style="clear: both; height: 0; overflow: hidden;"></div>
</div>
<script>
// Toggle the table of contents
$( "button#tocbutton" ).click(function() {
if ($("#tocbutton").css('right') == '25px') {
$( "#tocbutton" ).animate({right: '215px'},"slow");
$( "#toc.toc2" ).animate({right: '0'},"slow");
}
else {
$( "#tocbutton" ).animate({right: '25px'},"slow");
$( "#toc.toc2" ).animate({right: '-13rem'},"slow");
}
});
</script>
<script type="text/javascript">
// Create a back to top button
$('body').prepend('<a href="#" class="back-to-top">Back to Top</a>');
var amountScrolled = 300;
$(window).scroll(function() {
if ( $(window).scrollTop() > amountScrolled ) {
$('a.back-to-top').fadeIn('slow');
} else {
$('a.back-to-top').fadeOut('slow');
}
});
$('a.back-to-top, a.simple-back-to-top').click(function() {
$('html, body').animate({
scrollTop: 0
}, 700);
return false;
});
</script>
</body>
</html>