Generics

Generics are classes or interfaces which take one or more type parameters e.g. List{elementType=String} is a generic List class which takes a single type parameter (in this case String) indicating the type of the elements contained in the list.  In general, these parameters are then used instead of type names throughout the definition of the class or interface.  

In Jura, a generic class or interface is an extension of Class or Interface respectively which has additional properties corresponding to the generic parameters.  Jura does not restrict these properties to be types - they can be of any Jura type. When the generic class is constructed, it uses these properties to fill in the parameters of a template in a similar fashion to the way Programmatic Design Patterns are created.

Representing generic type instances

To understand how generic types can be represented in Jura, remember that an instance of a type is represented as:

Type:name{operations on type instance}

 For example, a Point instance called p is represented as:

Point:p{x=0 y=0}

The part in bold is the type of the instance, the remainder sets the name and properties (x and y) of that instance. Suppose the class Point were generic with a single type property coordType for the type of the co-ordinates (e.g. Integer, Float or Double).  A Point instance with Double type coordinates would then be written like this:

Point{coordType=Double}:p{x=0 y=0}

Now, both the type and the instance have properties.  The part in bold still defines the type and the remainder sets properties of that type.  Jura can tell the difference because any expression that appears directly before an opening brace '{' or named brace ':name{' will be interpreted as a type. In this example, both Point and Point{coordType=Double} are types.  To clarify, the instance being defined has class Point{coordType=Double} which in turn has class Point.  For comparison with Java, the instance "Hello" has class String which in turn a class Class.  The difference in Jura is that this hierarchy can be extended with new generic classes as needed.  To see how this can be done, here is an example of implementing a generic iterator interface.

A Generic Iterator interface

An iterator object allows the elements in a collection to be iterated over, e.g. to perform some operation on each.  If the elements in the collection are all of type T, then the corresponding iterator should return elements of type T, rather than of type Object (as iterators do in Java).  In other words, the iterator should use generics.

We can implement a generic iterator in Jura by extending the Interface class to have an elementType property and then, following successful validation, generating methods on the interface using this property:

Class:Iterator{extends=Interface
  doc={"Allows iteration over a collection of objects."}

  Property:elementType{type=Type doc={"The type of elements that are iterated over."}}

  Method:validate{
    doc={"Validates the iterator and constructs methods."}
    //
    super.validate()
    this={
      Method:hasNext{returns=Boolean doc={"Returns true if there are additional elements to iterate over."}}
      Method:next{returns=$elementType doc={"Returns the next element in the collection."}}
    }
  }
}

An instance of this generic type for iterating over String objects would then be represented as:

Iterator{elementType=String}

which is equivalent to the following interface

Interface:Iterator_String{
  Method:hasNext{returns=Boolean doc={"Returns true if there are additional elements to iterate over."}}
  Method:next{returns=String doc={"Returns the next element in the collection."}}
} 

Generic classes can themselves implement interfaces (and so can have abilities). For example, in Jura, the HasElementType ability is implemented by many generic collection classes.

A pattern, such as Template, can be used to make the above definition more readable:

Class:Iterator{extends=Interface
  doc={"Allows iteration over a collection of objects."}

  Property:elementType{type=Type doc={"The type of elements that are iterated over."}}

  Template{
    Method:hasNext{returns=Boolean doc={"Returns true if there are additional elements to iterate over."}}
    Method:next{returns=$elementType doc={"Returns the next element in the collection."}}
  }
}

This pattern transforms into exactly the same code as above.  It also allows for a separate validate() method to be defined.

In fact, the above definition of Iterator is not quite complete as it does not override the isAssignableFrom() and isCastableFrom() methods.  If these methods are not overridden, then the follow code will not compile:  

Local:it1{type=Iterator{elementType=Object}}
Local:it2{type=Iterator{elementType=String}}
it1=it2 {* Will fail at compile time if isAssignableFrom() is not overridden *}

In Jura, many generic collection classes delegate to a class which implements HasElementType and provides correct implementations of isAssignableFrom() and isCastableFrom().   For a collection class, isAssignableFrom() should return true if the supplied type is assignable to the generic type and its elementType property is assignable from the elementType of the supplied type.

Generic methods

Generic methods (methods where the type of a method parameter is itself a parameter) are not provided in Jura as it leads code which is effectively untyped. Instead, appropriate abilities should be used.

Generic classes in the prototype

In the prototype implementation, each generic instance is compiled to a separate Java class whose name is derived from the name and properties of that instance.