Java Layers Quick Reference Guide
Java Layers Home

JL Download

Installation Notes

Release Notes

Quick Reference

Code Generation

 

Introduction

This 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 Polymorphism

JL 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> {}  
class D<T; int i; boolean b> {}
interface I<T> {}

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>
D<String, 5, true>
I<double>

Both primitive and reference types can be used as actual type parameters.  Only literal values can be specified for primitive literals parameters. 

Constrained Parametric Polymorphism

Type 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> {}  
interface I<T extends LinkedList implements Runnable> {}

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
          implements Serializable, Runnable> {}

F-Bounded Polymorphism

JL 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>> {}

Mixins

Java Layers supports mixins, which are types with parameterized supertypes.

class M<T> extends T {}
interface I<U> extends U {} 

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 {}
class M3<T> implements T{}

Nested Parametric Types

In JL, nested types can also be parametric.  Here's an example:

class Outer<T>
{
  static class Inner<U> {T 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 Conformance

JL 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 Subtyping

The 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 {} }
class C<T extends Outer deeply> {T.Inner in;}

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 
{public class Inner extends Outer.Inner {}

Deep subtyping works in the same way for interfaces and their extends clauses as it does for classes.

Deep Interface Conformance

The 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 {} }
class C<T implements OuterIfc deeply
{T.Inner in = new T.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 
{public class Inner implements OuterIfc.Inner {} }

Using Deeply

The 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>
          implements Ifc1 deeply, Ifc2 deeply {}

class C<T extends deeply A, B>
          implements deeply Ifc1, Ifc2 {}

class C<T extends deeply A deeply, B deeply>
          implements deeply Ifc1 deeply, Ifc2 deeply {}

Using Propagate

The 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 {} }
class C<T extends Outer deeply> {T.Inner in;}


The Implicit This Type Parameter

In 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<>
{
  This _next;
}

class DoubleListNode<>
 extends SingleListNode<>
{
  This _prev;
}

Explicit This-Binding

The 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>
SingleListNode<:DoubleListNode<>>

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           
DoubleListNode<:SingleListNode<>> // Invalid This-binding
   

Binding This in Interfaces

Parameterized 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
class D<T> implements Ifc<> {}  // This = D<T>

Ifc<> // This = Object
  

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
class F<T> implements Ifc<:This> {} // This = F<T>

Ifc<:String> // This = String
  

Qualified This Usage

Nested types can refer to the This parameter of any enclosing parametric type by qualifying This with the simple name of the enclosing type.  This is similar to the way inner classes refer to this of their enclosing instances.  Below is an example of qualified This usage in JL.

class Outer<T>
{
 static class Inner1<U> {Outer.This oThis;}
}


Constructor Propagation

Though constructors are not inherited in Java, they can be propagated in JL.  Constructors modified with the propagate keyword interact with similarly modified constructors in subclasses to automatically generate new constructors.  These new constructors have signatures that combine the formal parameters of the propagated subclass and superclass constructors.  

Constructor propagation can be most easily be understood through an example.  Consider the following class definitions.

class C
{
 propagate C(int i){...}
}

class D extends C
{
 propagate D(String s){...}
}  

When D is compiled, the following Java code will be generated:

class D extends C
{
 D(String s, int i){super(i); ...}
}  

The Java code that results from constructor propagation combines the formal parameters of subclass and superclass constructors.  The subclass constructor's original formal parameters are followed by the superclass constructor's formal.  Parameters are renamed if necessary.  Propagation processing starts at the root of a hierarchy and proceeds to the leaf classes, accumulating formal parameters is it progresses.   

Constructor propagation also inserts a call to the superclass constructor in the body of the generated constructor.  Thus, constructors declared with the propagate modifier cannot contain explicit constructor invocations (i.e., this(...) or super(...)).  

If a superclass has 3 propagated constructors and a subclass has 4, the transformed subclass will have 12 constructors.  This multiplicative behavior means that care must be taken to avoid an explosion in the number of constructors, which will occur in deep class hierarchies whose classes each contain many propagated constructors.  If a subclass does not contain propagated constructors, no superclass constructors are ever propagated.

Constructor propagation mimics constructor inheritance in the case where the subclass has a no-argument propagated constructor.  The default constructor is treated as propagated by JL.  To illustrate this, consider the class E, which subclasses class C defined above.

class E extends C {}

When E is compiled, the following Java code will be generated:

class E extends C
{
 D(int i){super(i);}
}  

Last Modified:  08/28/2001 02:55 PM