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
- Covariance
- You can assign values of type
Tor any subtype ofTto an item of typeT. - Contravariance
- You can assign values of type
Tor any supertype ofTto an item of typeT. - Invariance
- You can assign values only of type
Tto an item of typeT.
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.
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.
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>.
