In object-oriented ABL, generic types are used in support of built-in collections classes and interfaces (Progress.Collections.*). Generic types allow for code reuse and strong compile-time type compatibility checking. This topic describes some of the concepts and terminology of ABL generic types. For information on .NET generic types, see Reference .NET generic types in Use .NET Classes in ABL Applications.

Generic type

A generic type is an object-oriented type (class or interface) that accepts one or more type parameters, allowing the class to defer the specification of such parameters until the class is declared and instantiated by a client (user) of the class. This allows the implementation to be re-used by many diverse types, instead of having to create a class for each distinct type, and without the cost of having to cast references at runtime.

With generics, you can define and use type-safe collections, allowing you to catch more problems at compile-time instead of at runtime.

Note: Support is not currently provided for user-defined generic types.

A generic type is denoted by a type name enclosed in angle brackets. For example, <T>. The "T" is a placeholder for the real type you use when you declare and create the class. By convention, a single letter is used, and more specifically "T" is used, unless multiple parameters are required for a particular class.

When programming with generic types, there are several places where the angle bracket notation is used. For example:
  • Variable declarations
  • Method declarations and invocations
  • NEW function
Type parameters are specified in class and interface definitions and method declarations. Type arguments are the types supplied by the client (user) of the generic type. The next sections describe these terms in more detail.

Type arguments in variable declarations

When you create a Progress.Collections.List<T>, you must specify the type of objects allowed in the list. You do this by supplying a type argument. A type argument is the type supplied by the client (user) of the generic type. When using a generic type, you must specify the same number of type arguments as there are type parameters defined in the generic type. You cannot instantiate a generic type without specifying type arguments.

The following code example shows two different variable declarations of the type List<T>:
using Progress.Collections.*.

var List<Ant> rListOfAnts.
var List<Person> rListOfPeople.
In this example, two different type arguments are used: Ant and Person. This allows the client code to have List<Ant> (referred to as "List of Ant") and List<Person> (referred to as "List of Person").

Referencing a generic type like this is referred to as "constructing the type". Therefore, a reference to a generic type that specifies a particular set of data types for its type parameters is referred to as a "reference to its constructed type name".

The compiler requires that each type argument has an "is-a" relationship to the corresponding type parameter. In other words, the argument must either be, inherit from, or implement the type parameter. Because the List<T> type defines a type parameter of type Progress.Lang.Object, List<T> can be passed in any type of object-oriented ABL or .NET object as a type argument, but not any scalar types.

After you declare a variable using the constructed type name, the compiler ensures that the type of the type arguments are used whenever the generic class uses the type parameter.

Method declarations and invocations

The following example shows the method declarations for the Add() and Get() methods of the Progress.Collections.List<T> class:
method public void Add (element as T).

method public T Get(index as integer).
When calling these methods, the type arguments supplied to the methods must exactly match the type parameter definitions.
In the following code example, the Add() method requires a Person for a List<Person> and the Get() method returns a Person.
var List<Person> rListOfPeople.
var Person currPerson = new Person("John Smith").

rListOfPeople:Add(currPerson).
currPerson = rListOfPeople:Get(1).
In this example, if you were to change List<Person> to List<Ant>, then both the Add() and Get() calls would not compile because the Person and Ant types are not compatible.

Type compatibility rules

The compatibility rules for object-oriented ABL types applies to generic types, except that the type arguments must match exactly. For example, the following ABL statement is allowed, because List<T> implements IList<T>, and the type arguments match exactly (Ant):
var IList<Ant> rListOfAnts = new List<Ant>.
However, assuming Ant has Insect in its hierarchy, whether as a super class or an interface, the following ABL statement is not allowed, because the type argument does not match exactly:
var IList<Insect> rListOfInsects = new List<Ant>.  //Compiler error
This restriction also applies when casting an object (with CAST or DYNAMIC-CAST).