| //// |
| Copyright (c) 2016 NumberFour AG. |
| All rights reserved. This program and the accompanying materials |
| are made available under the terms of the Eclipse Public License v1.0 |
| which accompanies this distribution, and is available at |
| http://www.eclipse.org/legal/epl-v10.html |
| |
| Contributors: |
| NumberFour AG - Initial API and implementation |
| //// |
| |
| |
| |
| .N4JS and Java |
| = N4JS and Java |
| :doctype: book |
| :notitle: |
| :toc: right |
| |
| == Introduction |
| |
| N4JS is an extension of ECMAScript. It is therefore as different from Java as ECMAScript is. There are additional features which |
| are quite similar to Java, actually making ECMAScript feel more like **Java**Script. In the following, we describe the most |
| important similarities and differences of Java and N4JS. |
| |
| |
| |
| == Similarities |
| |
| |
| The general idea of N4JS is to make ECMAScript as type safe as Java. For that reason, N4JS adds a static type system to ECMAScript. |
| Many concepts of this type system are similar to Java's type system. |
| |
| === Nominal Typing |
| |
| |
| N4JS supports both, <<../features/nominal-and-structural-typing#nominal_and_structural_typing,nominal and structural typing>>. Java has a nominal |
| type system and this is also the default mode in N4JS. When you just use a simple type reference, nominal typing (as in Java) is applied. |
| |
| |
| The following example should be easily understood by Java developers: |
| |
| |
| [source,java] |
| ---- |
| class A { public foo(): void {} } |
| class B extends A {} |
| class C { public foo(): void {} } |
| |
| function f(p: A) {} |
| |
| f(new B()); |
| f(new C()); // error: C is not a subtype of A. |
| ---- |
| |
| |
| This example will produce an error in the last line because `C` is not a nominal subtype of `A`. |
| |
| |
| Overriding of members in N4JS is quite similar to Java although N4JS allows for slightly more flexibility due to the underlying |
| ECMAScript semantics (see <<Function Subtyping>>). |
| |
| |
| === Generics |
| |
| The most important concept (taken more or less 1:1 from Java) is generics. There are a lot of languages providing support for |
| generics. Often there is limited support compared to Java, or different concepts are used. There are languages, for example, |
| which support generic types but not generic functions, or do not support wildcards. Languages such as Scala, for instance, have |
| different concepts for generics. |
| |
| |
| N4JS provides all features related to generics as Java does. Supported are: |
| |
| |
| * generic types and generic methods (and functions) |
| * type variables with lower bounds |
| * parameterization with wildcards (and upper and lower bounds) |
| |
| |
| N4JS even uses the very same https://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html[algorithm to infer type |
| variables as Java 8]. This algorithm is, of course, slightly adapted to support extra features of N4JS such as union types. |
| |
| |
| As a consequence, generics and parameterization should immediately be understood by Java programmers (as far as 'immediately |
| understood' can be applied to generics). |
| |
| |
| At the moment, the N4JS type system has some known issues when substituting type variables and instantiating wildcard |
| expressions. For the latter, Java uses a technique called "capture conversion" while N4JS implements a slightly simpler |
| one called "existential types". Future releases of N4JS will fix these issues. |
| |
| === Interfaces and Abstract Declarations |
| |
| |
| ECMAScript 2015 introduces classes similar to Java. In both languages, single inheritance is used. Although this simplifies |
| many scenarios, the limitations of single inheritance are often problematic. In Java, many of these problematic cases can |
| be solved with interfaces. N4JS also provides the concept of interfaces, allowing for classes to implement multiple interfaces. |
| Interfaces in N4JS allow for the definition of default methods as in Java 8, therefore overcoming most of the problems |
| introduced by single inheritance. Related to interfaces are abstract methods and, consequently, abstract classes which are |
| also supported in N4JS similarly to Java. |
| |
| |
| N4JS adapts the `instanceof` operator so it can be used in combination with interfaces at runtime. |
| |
| === Access Modifiers |
| |
| |
| When designing larger systems and frameworks, limiting access to certain elements is important for maintainability. Similar |
| to Java, N4JS introduces access modifiers to limit visibility of types and members. The following table shows the access |
| modifiers of N4JS compared to Java: |
| |
| |
| |======================= |
| ^|*Java* ^|*N4JS* 2+| *Remark* |
| 2+^|Public 2+| Types and members, similar semantic |
| 2+^|Protected 2+| Members only, similar semantic |
| ^|Package |
| ^|Project 2+| See below |
| |private |
| |members only, similar semantic |
| |======================= |
| |
| While it is possible to organize modules in folders which could be interpreted as packages, N4JS does not really support the |
| notion of packages. Instead, it uses projects to encapsulate coherent functionality. Project is the default visibility in |
| N4JS as package is in Java. The semantics are also similar in both cases: elements (types or members) can only be accessed |
| from the same container (project in N4JS and package in Java). |
| |
| |
| == Differences |
| |
| === Auto-Boxing |
| |
| In Java, primitive types can be auto-boxed to their corresponding object types (and vice versa). This is also true for |
| ECMAScript but there are subtle differences which might lead to misconceptions as shown in the following example: |
| |
| |
| [source,n4js] |
| ---- |
| let bool: boolean = false; |
| let Bool: Boolean = new Boolean( false ); |
| |
| console.log( bool.valueOf(), bool ? "true" : "false"); |
| console.log( Bool.valueOf(), Bool ? "true" : "false"); |
| ---- |
| |
| |
| Probably surprising for Java programmers, this would print out |
| |
| |
| [source,n4js] |
| false 'false' |
| false 'true' |
| |
| In order avoid these problems, N4JS does not support auto-boxing. Instead, primitives and object types have to be |
| explicitly converted using either the `constructor`, `Object.valueof` or other related methods. |
| |
| |
| === Function Subtyping |
| |
| Function (or method) subtyping comes into play in two cases: method overriding and passing functions as arguments |
| (callbacks, for example). In both cases, the type checker has to ensure that a function `*f*` is compatible with |
| another function `*f'*` (in other words, that the type of `*f*` is a subtype of the type of `*f'*`). In Java this is called |
| "override compatible". |
| |
| In Java, a method `*f*` is override compatible to a `*f'*` if and only if |
| |
| . it has the same name |
| . it has the same number of parameters |
| . the type of each parameter of `*f*` must be a supertype of the corresponding parameter of `*f'*` |
| . its return type is a subtype of the return type of `*f'*` |
| |
| In ECMAScript, it is possible to call a function of a method with more or less arguments than declared |
| formal parameters. Calling a function with less arguments is not allowed in N4JS (unless the parameters |
| are declared as optional). The definition of "override compatible" is, therefore, a little bit different |
| in N4JS. |
| |
| In N4JS,`*f'*` is override comptabible to `*f*` (or its type is a subtype of the type of `*f*`), if |
| |
| . it has the same name (in case of method override) |
| . it has the same number or less of parameters, or superfluous parameters are optional |
| . the type of each parameter of `*f'*` must be a subtype of the corresponding parameter of `*f*` |
| . its return type is a subtype of the return type of `*f*` , or `*f*` has no return type (it's void). |
| |
| For example, the following code is correct in N4JS while it would cause compile errors in Java: |
| |
| [source,n4js] |
| class A { |
| foo(s: string): void {} |
| } |
| class B extends A { |
| @Override |
| foo(): number { return 0 } |
| } |
| |
| |
| === Overloading |
| |
| |
| There is no method overloading in ECMAScript and therefore there cannot be overloading in N4JS. In order to 'emulate' |
| overloading to a certain degree, union types and optional parameters can be used. |
| |
| |
| === Static Members |
| |
| In Java, a static member of a class can be accessed either |
| |
| |
| |
| . via the declaring class (or a subclass) |
| . via an instance |
| |
| |
| In N4JS, a static member can only be called via the declaring class. |
| |
| |
| Note that the `this` literal is bound to the class (to the constructor function, in fact). This enables |
| static polymorphism as shown in the next example: |
| |
| |
| [source,n4js] |
| ---- |
| class A { |
| public static s() { console.log("A.s"); this.t(); }; |
| public static t() { console.log("A.t"); }; |
| } |
| class B extends A { |
| @Override |
| public static t() { console.log("B.t"); }; |
| } |
| |
| A.s(); |
| B.s(); |
| ---- |
| |
| This will print out |
| |
| |
| [source] |
| A.s |
| A.t |
| A.s |
| B.t |
| |
| The last line in particular may be surprising for Java programmers. |