A type parameter is a placeholder for an actual type, which gets specified by the code that’s using the generic.
If you imagine your generic as a light fixture, the type parameter would be the socket where the light bulb goes. Just as you can choose from a variety of different light bulbs to put into that socket, the calling code can choose from a variety of different types to pass to a generic.
Within your generic, you first declare the type parameter, and then you can reference it in multiple places throughout the function, class, or interface.
This is one of the simplest examples of a generic - it’s a class that wraps any object. Here we’ve got a simple declaration where we tell the compiler that this class is a generic with a type parameter called
T. Then the the type parameter is referenced as the type of
item (which also has a public getter and setter).
Classes, functions, extension properties, and interfaces can all be generic. To see examples of those, check out the article dedicated to type parameter declarations.
Although technically any valid identifier would work as the name of a type parameter, by convention, type parameter names are often a single, upper-cased character, just like in Java. For more information about Java’s naming conventions for type parameters, see the section, Type Parameter Naming Conventions in the Generic Types tutorial.
Constraints: Limiting the Types
Just like you can’t put a T3 halogen tube light bulb into a standard E26 light socket, you might want to limit the types that calling code can supply to the type parameter. To do this, you use a type parameter constraint.
class NumberBox<T: Number>(var item: T)
Variance: Subtyping Generics
By default, subtyping doesn’t apply to generic classes or interfaces. But by using a variance annotation, you can introduce it. Just keep in mind that it limits what you can do with the type parameter.
class Box<out T>(val item: T)
Do you want to see if an object has the same type as the type parameter? Or to get a class reference from it?