There are restrictions with user-defined generics in regard to the following:

  • .NET objects
  • CATCH statement
  • ENUM statement
  • TYPE-OF function
  • CAST function
  • GET-CLASS function
  • NEW statement
  • STATIC members

The details are described in the following sections.

.NET objects

.NET objects are not allowed as type parameters or type arguments. For example:

CLASS SimpleGenericType<T as System.Object>. //invalid

CATCH statement

The CATCH statement does not allow a generic type or a type parameter. For example, you get a compiler error if you write CATCH statements like the following:

CATCH appErr AS myAppError<Dog>: //compiler error
...

CATCH appErr AS T: //compiler error
...

ENUM statement

ENUM types cannot be generic.

TYPE-OF and CAST functions

You cannot use a type parameter by itself in the generic class implementation where a type would be specified for the TYPE-OF() and CAST() built-in functions. For example:

CLASS MyGeneric<T AS Animal>: 
  METHOD VOID makeNoise(myPLO AS Progress.Lang.Object):      
    IF TYPE-OF(myPLO, T) //compiler error: T is a type parameter
    THEN
      CAST(myPLO, T):Sound(). //compiler error
  END METHOD.
END CLASS. 

In such cases, you can use the constraint type, instead of T. For example:

CLASS MyGeneric <T AS Animal>: 
  METHOD VOID makeNoise(myPLO AS Progress.Lang.Object): 
      IF TYPE-OF(myPLO, Animal) THEN
         CAST(myPLO, Animal):Sound().
  END METHOD. 
END CLASS.

GET-CLASS function

You cannot use a type parameter by itself in the generic class implementation where a type would be specified for the GET-CLASS() built-in function. For example:
VAR Progress.Lang.Class myCls = GET-CLASS(T). //compiler error

NEW statement

The NEW statement does not allow the type parameter. The compiler cannot perform checking on the signature of the constructor since the real type is not known until the client of the generic class specifies the type argument. For example:
VAR T newInstance = NEW T(). //compiler error
  
VAR T newInstance2 = NEW T(10). //compiler error

You can use dynamic programming and/or reflection to perform tasks that are dependent on the type of the type argument specified by the client of the generic type. Progress.Lang.Class contains the GetTypeArguments() method, which you can use to get the type of the type parameters. For example:

CLASS myList<T AS Progress.Lang.Object>:
  DEFINE PRIVATE VARIABLE arg1plc AS CLASS Progress.Lang.Class.
  CONSTRUCTOR myList():
    VAR Progress.Lang.Class[] myArgs.
    myArgs = THIS-OBJECT:GetClass():GetTypeArguments().
    arg1plc = myArgs[1]. //store the type of the 1st Type Argument
  END. 
  METHOD T makeNewObj():
    /* dynamically new an object whose type is the Type Argument supplied */
    RETURN arg1plc:new().
  END METHOD.
END CLASS.
Note that this restriction does not apply if the type parameter is used as a type argument and the type constraints are compatible. In that case, the type parameter is being passed through to another generic type. In the following example, assuming the type constraint for the type parameter of myListIterator is Progress.Lang.Object, the NEW statement is allowed because the type constraint for T is Progress.Lang.Object:
CLASS myList<T AS Progress.Lang.Object>: 
  DEFINE PUBLIC PROPERTY Iterator AS myIIterator<T> NO-UNDO 
    Get(): 
      RETURN NEW myListIterator<T>(THIS-OBJECT). //valid 
    END. 
END.

The following is a more complex hierarchical example which does compile:

CLASS myBaseClass:
END.

CLASS myDerivedClass INHERITS myBaseClass:
END.

CLASS mySimpleGeneric<T as myBaseClass>: 
END.

CLASS DerivedGeneric<Y AS myDerivedClass>: 
  METHOD PUBLIC VOID somemeth(elem AS Y):
    VAR mySimpleGeneric<Y> simpleGen;
  END.
END.

In the example above, mySimpleGeneric has a type parameter with the myBaseClass type constraint. Since the type parameter constraint in DerivedGeneric is myDerivedClass, the variable declaration in somemeth is valid, as myDerivedClass is a subclass of myBaseClass. If the types are not compatible, the compiler raises an error.

In summary, a type parameter may only be referenced in built-in functions TYPE-OF, CAST, GET-CLASS, and NEW if it is used as a type argument to a generic type and it follows the type compatibility rules.

STATIC members

Generic classes themselves are not allowed to define STATIC members: methods, variables, properties, events, and so on. It is valid to have static members in a non-generic class in the hierarchy, but the generic type itself cannot have static members.

However, you can access inherited static members using the TYPE-NAME syntax using the generic type, but understand that they are in the non-generic class. A clearer way to reference them is using the actual type that defines them. For example, given these classes:

CLASS myroot:
  DEFINE PUBLIC STATIC PROPERTY myprop AS INT GET. SET.
END.

CLASS SimpleGeneric<T AS Progress.Lang.Object> INHERITS myroot.
END.

You can write either one of these statements, but the latter is clearer:

myvar = SimpleGeneric<Employee>:myprop.

myvar = myroot:myprop.