Generic methods that reify a type

To support parameterized methods that reify the generic type, Gosu provides the keyword reified. This keyword instructs Gosu to make information about the runtime type of the type parameter available. Gosu requires the keyword reified as a modifier on generic functions that are equivalent to parameterized Java functions that have type erasure. Use the modifier reified on a parameterized function that has any of the following characteristics, where T is the token for the type parameter of a generic type:

  • The method uses the new keyword to create an instance of T.
  • The method uses the new keyword to create an instance of a generic type and includes the <T> qualifier on the type name.
  • An expression in the method uses the generic type with the typeis or typeof operator.
  • The method contains code that uses as T to cast a value to the generic type.
  • The method overrides a reified method in a parent class or implements a reified method in an interface.
  • The method calls another reified method and passes an argument of a generic type to that method.

The modifier reified is optional on a parameterized method that does not have any of these characteristics.

Gosu forbids the modifier reified on methods that are not generic.

For a function that requires this modifier, the signature looks like the following examples:

reified function cast<N>(type : Type<N>) : List<N>
reified function partition<Q>( partitioner(elt : T):Q ) : Map<Q, List<T>>
static reified function returnLast<T>(a : ArrayList<T>) : T

Although some parameterized functions do not require the reified keyword, good practice is to make such methods reified. Using the reified keyword prevents error conditions if future changes to the code in the method or code that overrides the method cause reification of the parameterized type.

Using a generic collection in a generic method

A generic method that uses new to create a generic collection requires the reified keyword only if the method instantiates an object of the generic type. The reified keyword is optional if the new collection is empty. In this case, Gosu does not require the type of the collection. The reified keyword is also optional if the method populates the collection with existing objects. In this case, Gosu infers the type of the collection from the types of the objects. For example:

// reified is optional because the ArrayList is empty
reified function emptyListE<E>() : List<E> {
  return new ArrayList<E>()
}

// reified is optional because the code populates the ArrayList with an existing object
function oneListFromE<E>(e : E) : List<E> {
  return new ArrayList<E>({e})
}

// reified is required because the code creates a new object of type E to populate the ArrayList
reified function oneListE<E>() : List<E> {
  return new ArrayList<E>({new E()})
}

Example: Basic use of a reified method

In the following code, the class is not parameterized but one method is parameterized. Casting this to T requires the method to be reified:

class BasicReificationTester {
  public reified function returnAsT<T>(a : ArrayList<T>) : T {
    return this as T
  }
}

In the method’s Gosu code, the symbol T is used as a type. Gosu matches T to the type of the collection passed into the method.

The following code uses this class and the reified method:

var myStrings = new ArrayList<String>(){"a", "abcd", "ab", "123"}
var t = new BasicReificationTester()
var stringT = t.returnAsT(myStrings)

print("Returned item type: " + typeof stringT)

The variable stringT is strongly typed as String, not Object.

Example: Reification by accessing type properties

Accessing Gosu properties of the parameterized type requires reification. The following method accesses a Gosu property of the T type and requires the reified keyword:

reified function typeofClassT<T>(t : T) {
  var typ = T.TypeInfo
}

Example: Parameterized method that does not require the reified keyword

The following method is parameterized but does not require reification, so the reified keyword is optional:

// Return whether the argument is a String
public function isStringType<T>(t : T) : boolean {
  return t typeis String
}

If future code changes to the code in this method or code that overrides this method cause reification to be necessary, the methods would not compile. Good practice is to use the reified keyword.

// Return whether the argument is a String
public reified function isStringType<T>(t : T) : boolean {
  return t typeis String
}

Example: Another parameterized method that does not require the reified keyword

In the following example, the class Genericstest is parameterized:

class GenericsTest<T> {
  // Print out (for debugging) and then return the first item in the list, strongly typed
  public function printAndReturnFirst(aList : ArrayList<T>) : T {
    print(aList[0])
    return aList[0] 
  }
}

The following method is parameterized but does not require reification, so the reified keyword is optional:

// Create an unspecified type of GenericsTest
public function makeANewGenericOfUnspecifiedType<T>(aList : ArrayList<T>) {
  var x = new GenericsTest().printAndReturnFirst(aList)
}

Because the code does not specify the type of GenericsTest, Gosu uses the the lowest possible bound of GenericsTest for the type of the variable x, which is Object.

If future modifications to the code in this method or code that overrides this method cause reification to be necessary, the methods would not compile. Good practice is to use the reified keyword.