| Java Layers Quick Reference Guide | ||
| Java
Layers Home
|
IntroductionThis Quick Reference Guide provides a short description of all the implemented language extensions in the current version of Java Layers. We briefly describe JL syntax; for a more detailed discussion, please refer to the JL Specifications. The main topics covered in this guide are:
Parametric PolymorphismJL class and interface declarations can be parameterized with type parameters and primitive literal parameters. Below are some examples, notice that a semicolon separates parameters in declarations. class C<T; U; V>
{} There are 8 types primitive literals that can be used in parametric type declarations: boolean, byte, char, double, float, int, long, short. Instantiations of parametric types must provide actual parameters for all formal parameters that appear in the declaration. Below are instantiations of the three types shown above. C<String, int, LinkedList> Both primitive and reference types can be used as actual type parameters. Only literal values can be specified for primitive literals parameters. Constrained Parametric PolymorphismType parameters can be constrained or bounded in a type declaration. Here are examples of simple type constraints. class C<T extends FileReader;
U implements Serializable> {} In class C above, the actual type bound to type parameter T must extend FileReader, and U's actual type must implement the Serializable interface. Similarly, interface I's type parameter, T, must both extend LinkedList and implement interface Runnable. Type parameters can be declared with any number of constraints, as seen here in class D: class D<T extends LinkedList,
AbstractSequentialList F-Bounded PolymorphismJL supports F-bounded polymorphism, in which type parameters are used within constraint clauses, as illustrated in the following example: class E<T implements Ifc<T>> {} MixinsJava Layers supports mixins, which are types with parameterized supertypes. class M<T> extends T {} The above two types, C and I, extend the actual type parameters that are bound to formal type parameters, T and U, respectively, during instantiation. Note that neither T nor U could be bound to a primitive type in this example. Other variations on mixins are also possible in JL: class M2<T> extends
T.Inner {} Nested Parametric TypesIn JL, nested types can also be parametric. Here's an example: class Outer<T> A type parameter is unique within its scope. In the above example, class Inner could not declare its own type parameter named T. Inner can, however, use T that was declared in its enclosing class, Outer. Both classes and interfaces, whether they are parametric or not, may contain nested parametric classes, interfaces or both. IMPLEMENTATION NOTE: The current implementation of JL does not calculate the types of all expressions. This has the unfortunate effect of making non-static (or "inner") parametric classes difficult to use. For details, see the Release Notes and a description of a work-around. Deep ConformanceJL adds the deeply and propagate keywords to support Deep Subtyping and Deep Interface Conformance, which we abbreviate with the single term Deep Conformance when speaking generally. These concepts were first formalized by Yannis Smaragdakis and, informally, can be understood to mean that the public nested structure and semantics of a type are preserved or mirrored in subtypes. Deep SubtypingThe deeply keyword can be added to any extends clause to require that deep subtyping be enforced. Here's an example of deeply used an extends constraint clause. class Outer {public class Inner
{} } By specifying in the above example that the type bound to type parameter T be a deep subtype of Outer, we are guaranteed that class T.Inner exists and that it extends Outer.Inner. When deeply is used in an inheritance extends clause, as shown below, it means that the declaring subclass (D) must define all nested public classes in the class it deeply extends (Outer, as previously defined), and that these nested classes in the subclass must inherit from their corresponding nested class in the superclass (D.Inner must subclass Outer.Inner). class D extends Outer
deeply Deep subtyping works in the same way for interfaces and their extends clauses as it does for classes. Deep Interface ConformanceThe deeply keyword can be added to any implements clause to require deep interface conformance. Here's an example of deeply used in an implements constraint clause. interface OuterIfc {public
interface Inner {} } By specifying in class C that the type bound to type parameter T deeply conform to interface Outer, we are guaranteed that T.Inner is a public nested class that implements OuterIfc.Inner. Once T is bound the above code will be compile, assuming that a no-argument constructor is defined for T.Inner. When deeply is used in an implements inheritance clause, as shown below, it means that the declaring class (D) must define a corresponding public nested class for each nested interface in the interface to which it (D) deeply conforms (OuterIfc, as previously defined). Thus, nested class D.Inner must exist and implement OuterIfc.Inner. class D implements OuterIfc
deeply Using DeeplyThe deeply keyword can appear only in extends or implements clauses. In these clauses, if deeply immediately follows the extends or implements keyword, then deep conformance is in effect for all types that appear in the clause. On the other hand, if deeply immediately follows a type in an extends or implements clause, then deep conformance is in effect only for that type. To illustrate how keyword placement effects semantics, the following three declarations are equivalent. class C<T extends A deeply, B
deeply> class C<T extends deeply A,
B> class C<T extends deeply A
deeply, B deeply> Using PropagateThe propagate keyword can be used as a modifier on nested types to explicitly include the nested type in deep conformance processing. This allows nested types with package or protected access to be treated by the deeply keyword in the same way as public nested types. Below is an example of a propagated nested type Outer.Inner with package scope. As long as the type bound to T, class C and class Outer are in the same package, the code will compile and deep conformance guarantees that T.Inner extends Outer.Inner. class Outer {propagate class Inner
{} } The Implicit This Type ParameterIn Java, the this reference always refers to the current runtime object. In JL, the This type parameter refers statically to the most specialized class in a hierarchy of parametric classes. This is an implicit type parameter in all parametric types, and can be implicitly or explicitly bound. This is always bound to a class type, never an interface type. The This keyword can only be used inside the definition of a parametric type, including in the part of the type declaration that precedes the body. In the following example code, This is implicitly bound and used to virtualize the types of the _next and _prev references. When an instance of the class SingleListNode is created, This is implicitly bound to the type SingleListNode. On the other hand, when an instance of the class DoubleListNode is created, This is implicitly bound in both SingleListNode and in DoubleListNode to type DoubleListNode. class SingleListNode<> class DoubleListNode<> Explicit This-BindingThe This parameter can also be explicitly bound by the programmer by specifying the type to be bound to This as the first actual type in an instantiation and preceding it with a colon. Here are examples of instantiations that explicitly provide a valid This-binding. SingleListNode<:SingleListNode> The first instantiation above simply makes explicit the binding JL will implicitly perform for This. The second instantiation designates a subtype of SingleListNode as the This-binding in SingleListNode, which is required by JL. To be more precise, if This is used in a parametric class or any of its superclasses, then JL requires a valid This-binding in the parametric class. For parametric classes, a valid This-binding is one in which This is bound to the class's instantiation or some subclass of the class's instantiation. Implicit This-bindings respect the subclass condition and are always valid. When This-binding are explicitly specified, it's the programmer's responsibility to satisfy the subclass condition. Here are examples of This-bindings that would be rejected by the JL compiler because the class types bound to This are not subclasses (inclusive) of the instantiated class. SingleListNode<:String> //
Invalid
This-binding Binding This in InterfacesParameterized interfaces also have implicit This type parameters, but the binding rules for interfaces are simpler than those for classes. This is defined as the class type of the most specialized type in a hierarchy of parameterized classes. When a parameterized interface is instantiated, This is implicitly bound to the most specialized class type that implements the interface. Parameterized interfaces can also be instantiated in a standalone context outside the scope of an implements clause. In standalone contexts, This is bound implicitly to java.lang.Object. In the examples below, Ifc is some parameterized interface. In the class declaration for C, This in Ifc is implicitly bound to C. In the class declaration for D, This in Ifc will be bound to D<T> when D is itself instantiated. The last line in the example represents a standalone instantiation of Ifc, so This is implicitly bound to java.lang.Object. class C implements Ifc<>
{} // This = C The programmer can also explicitly specify a This-binding for a parametric interface. The JL compiler does not enforce subtype constraints on the This-binding of parameterized interfaces, so any class type will be excepted. The examples below illustrate explicit This-bindings for parameterized interface Ifc. In the declaration of class E, the This-binding for Ifc is explicitly set to class Date. In the declaration of class F, the This-binding of Ifc is set to the This-binding of F<T> because :This refers to the in-scope This parameter of F. Since F's This-binding is implicitly set to F<T>, that's the type bound to This in Ifc. Finally, the last example illustrates a standalone instantiation of Ifc with an explicit This-binding. class E<T> implements Ifc<:Date>
{} // This = Date
|
|