In computer programming, generics are parameter types specified for the programming language construct.
In Java and Kotlin, generics are available for classes, functions, and interfaces definitions that you write in your source code.
Generics are marked with angle brackets <>
after the construct name. The following example shows a Kotlin class
named Car
that has a generic:
class Car<T>
The generic type <T>
above is actually a placeholder type, and you can replace the T
with any valid Kotlin type when you instantiate a new object from the class.
The following examples are both valid Car
instances of type String
and Int
respectively:
val car = Car<String>()
val anotherCar = Car<Int>()
Now that you know what a generic is, let’s learn why it can be useful for your Kotlin application next.
The advantage of using generics in Kotlin
A class
defined with a generic type allows you to create a more flexible class
that can do more than a class without one.
To illustrate the benefits of using a generic, imagine you have a Car
class with a name
parameter for its primary constructor as shown below:
class Car(val name: String)
Now each time you create an instance of Car
class, you will always have a name
property of String
type
val car = Car("Tesla")
println(car.name) // Tesla
If you try to pass an Int
into the name
parameter, an error will be thrown during compile time as follows:
val anotherCar = Car(2)
// The integer literal does not conform to the expected type String
Since class
parameters need to have type annotations, you can only define one type to be accepted by the class
. It’s either a String
parameter or an Int
parameter.
This is where a generic can help you out. By adding a placeholder type for your class
you can define the type of your class
parameters later when you create an object of it.
With the following class
definition, the name
parameter would have the type you define in the instantiation:
class Car<T>(val name: T)
Now the T
can be replaced with a String
or an Int
as you require:
val car = Car<String>("Tesla")
println(car.name) // Tesla
val anotherCar = Car<Int>(2)
println(anotherCar.name) // 2
The <T>
is not any special syntax of Kotlin. It’s just a convention used when defining generic types.
The conventions for Kotlin generics are as follows:
E
for elementT
for typeK
for keyV
for value
You’ll see the conventions above used extensively in Kotlin official documentation as follows:
fun <T> arrayListOf(): ArrayList<T>
// or
interface List<out E> : Collection<E>
Additionally, you can remove the generic type during instantiation and let Kotlin infer the parameter type from the argument(s) you passed.
Consider the following example. The generic type is omitted when the Box
object was created:
class Box<T>(val value: T)
val box = Box("Chocolate")
println(box.value) // Chocolate
val anotherBox = Box(9)
println(anotherBox.value) // 9
Some class
in Kotlin also implement generics to help you in writing a flexible application.
For example, you can create an array of String
in Kotlin as follows:
val stringArr = arrayOf<String>("London", "York", "Bristol")
But since Kotlin is smart enough to infer the type of your array as <String>
, you can remove it during the declaration:
val stringArr = arrayOf("London", "York", "Bristol")
Finally, you can add generic type to a function as follows:
fun <T> getData(data: T) {
println(data.toString())
}
getData<Int>(5) // 5
To conclude, generics allow you to create classes, functions, or interfaces that can operate on different types of objects.
Although generics help you create a more flexible program, the use of generics also increases the complexity of your application.
You should use a generic type only when you know that you’re going to need an object instance of a single class
or a function that needs to operate on multiple types.
My recommendation is to always define explicit types for your Kotlin application, and only add generics further down the road as your application grows.