Compatibility of Gosu generics with Java generics

Gosu generics are compatible with generics implemented in Java version 1.8. You can use Java utility classes designed for Java 1.8 generics and even extend them in Gosu.

No support for super syntax in Gosu generics

There is one exception in syntax for Java-Gosu compatibility, which is that Gosu does not support the syntax <? super TYPE>.

Reification of Gosu generics

One important difference between Gosu and Java is that Gosu generics are reified. Reified Gosu generics retain the actual specific type at run time. For example, at run time, you can check whether an object is an instance of PetGroup<Cat> or PetGroup<Dog> including the information in the angle brackets.

Java generics have type erasure, in which generics lose the generic parameter information at run time. Java generics maximize compatibility with older Java code that did not support generics.

Gosu does not reify the type of a generic Java object. If you need the reification behavior for a Java class that Gosu provides for Gosu classes, wrap the Java class in a Gosu class.

Example of reifying a Java type

The following Gosu class uses a Java HashMap object. This object is not reified.

class MyTester {
  // Return a map from two lists
  // This signature returns a Java object that has its type erased
  public reified function mapArray<K,V>(kArray : ArrayList<K>, vArray : ArrayList<V>) : HashMap<K,V> {

  // This signature returns a reified Gosu object
  // public reified function mapArray<K,V>(kArray : ArrayList<K>, vArray : ArrayList<V>) : MyMapper<K,V> {

    var theMap : HashMap<K,V>
    for (v in vArray index i) {
      theMap.put(kArray[i], v)
    }
    return theMap
  }
}

In the mapArray method, the symbols K and V are used as types and Gosu matches K and V to the types of the collections passed into the method.

The following code uses this class:

var myKeys = new ArrayList<Integer>(){1, 2, 3, 4}
var myStrings = new ArrayList<String>(){"a", "abcd", "ab", "123"}
var t = new MyTester()
var myMap = t.mapArray(myKeys, myStrings)

print("My map is a ${typeof myMap}")

The variable myMap is typed as Hashmap<java.lang.Object, java.lang.Object>.

To cause reification of the myMap variable, define a Gosu class that wraps HashMap.

class MyMapper<K,V> extends HashMap {

}

Comment out the method signature in the MyTester class that returns a HashMap object and uncomment the signature that returns a MyMapper object. In the code that uses the MyTester class, the variable myMap is now typed as MyMapper<java.lang.Integer, java.lang.String>.

See also

Variance and Gosu generics

Variance is the assignability of one object of a generic type to another. Three types of variance exist:
Covariance
You can assign values of type T or any subtype of T to an item of type T.
Contravariance
You can assign values of type T or any supertype of T to an item of type T.
Invariance
You can assign values only of type T to an item of type T.

The context of an object's usage implies the variance of that object. Typically, Gosu can infer the appropriate variance. Gosu can determine variance even for an object that is used by a Java class rather than a Gosu class. This ability to infer variance is important for passing blocks as arguments to methods. Gosu uses the block type to infer variance, and therefore can determine that values passed to a block and assigned from the block's return value are valid. Java's lambdas must use function interfaces to supply the variance because Java does not infer variance. Gosu does not require this complexity and therefore Gosu generics are less complicated to use than Java generics. If you use Java lambdas from Gosu code, Gosu inspects the underlying Java methods to infer the correct variance.

In Java, calling a generic method or using a generic type requires explicit specification of variance by using the wildcard syntax. The following Java example shows a use of the Function type as a method parameter in the Stream interface:
<R> Stream <R> map(Function <? super T, ? extends R> mapper)
The wildcard syntax enforces T as contravariant and R as covariant. The equivalent method declaration in Gosu does not require the wildcard syntax because Gosu correctly infers the variance:
function map <R>(mapper(elt : T) : R) : List<R>

This method definition is in the CoreIterableEnhancement that Gosu provides for java.lang.Iterable. Gosu can also directly return a List object because Gosu does not require the intermediate Stream object that Java uses to overload the usage of the map method onto a collection type.

About covariance

Covariance means that an item of type T can be assigned values of type T or any subtype of T. For example, return values from a method are typically covariant from the point of view of the method. Your Gosu code stores the result in a variable that is assignable from the return type of the method.

Similarly, Gosu arrays are covariant in the same way as Java arrays. The following code shows that you can assign a value of any type that extends Number to an array of Number:

var numbers = new Number[3]
numbers[0] = new Integer(10)
numbers[1] = new Double(3.14)
numbers[2] = new Byte(0)

Covariance also allows the following example because an array Integer[] is a subtype of an array Number[]:

var myInts = new Integer[] {1,2,3,4}
var myNumber : Number[] = myInts

The following code compiles but generates an ArrayStoreException exception at run time:

myNumber[0] = 3.14 // Attempted heap pollution

The array store exception occurs because an Integer array has been assigned to myNumber and 3.14 is a double, which cannot be cast to a subtype of Integer. The exception occurs because the array is an array of Integer objects, not because the array access occurs through a Number reference.

Gosu generics are covariant by default. For example, this Gosu assignment works as expected.
var list : List<Object> = new ArrayList<String>()

Because Gosu applies covariance to this assignment, the wildcard syntax that Java requires is not necessary. Gosu does not use the ? wildcard syntax anywhere.

In Java, the equivalent assignment is illegal because the type arguments are invariant without wildcard qualification:

// Does not compile because List<Object> and ArrayList<String> are not related
List<Object> list = new ArrayList<String>();

// This line compiles because the wildcard qualification explicitly accepts subclasses
List<? extends Object> list = new ArrayList<String>();

About contravariance

Contravariance means that an item of type T can be assigned values of type T or any supertype of T. For example, input parameters to a method are typically contravariant from the point of view of the method. Your Gosu code provides an argument that is assignable to the type of the parameter.

About invariance

Invariance means that an item of type T can be assigned values of only type T and cannot contain either subclasses or superclasses of type T. Java generics are invariant by default.

For example, in Java, a List<T> is invariant on the type T of the list. You can assign only an item of type ArrayList<Number> to a variable of type ArrayList<Number>. You cannot assign an item of type ArrayList<Integer> to a variable of type ArrayList<Number>, nor can you assign an item of type ArrayList<Number> to a variable of type ArrayList<Integer>. Java does not treat the two types as being related.

Gosu is not invariant in this case, but is covariant. In Gosu, you can assign an item of type ArrayList<Integer> to a variable of type ArrayList<Number>.