Week 105 — What are generics and how can they be used?
Question of the Week #105
What are generics and how can they be used?
8 Replies
Generics allow specifying placeholders for types in classes and methods.
If a class specifies a generic/type parameter using the
<T>
syntax, it can use the type T
for variables, parameters, return types and superclasses/interfaces.
When an object of that class is created, all generics should be assigned to concrete types. Generics cannot be assigned to primitives (as of JDK 23 which is the current Java version at the time of writing this).
Methods can also declare generic types by specifying them before the return type:
Wildcards can be specified using the
?
-symbol meaning "any fixed type" whereas ? extends Temporal
refers to "any fixed subtype of Temporal
". It is also possible to use the super
keyword to specify bounds requiring a supertype.
Generics can have bounds specifying that the types must be within these bounds.
These bounds can also make use of wildcards. For example, it is possible to require a generic type to extend Collection
of some Temporal
📖 Sample answer from dan1st
Generics enables developers to define functions, classes, and interfaces that can work with multiple data types, without the need for explicit type casting or separate implementations for each type. Example
Submission from ig.imanish
Generics is used for type templating where the return type of a method can be different types or class uses different types of members. My favourite example of a good usage of generics is with Transformers or a Provider or List. You're welcome ( ̄︶ ̄)↗
Submission from klone_king
Generics are a language feature that was added to Java 5. They allow code to work with objects of any type (or a constrained type), rather than a single specific type.
The classic use case for generics are collections of objects. As an example, think about the
java.util.List
interface. Without generics, there would be only 2 possibilities for the API:
1. Methods like add
and get
would operate on Object
, so that any possible object could be passed/returned
2. Methods like add
and get
would need to be overridden for every possible class
Obviously, number 2 is not practical (and may not even be possible). So, before generics, the List
interface operated on Object
. This works, but is not type-safe. There is nothing preventing someone from inserting a String
into a list that is expected to contain Integer
s. This would actually work on the add
call. But down the road, when objects are read from the list, the client would have no way of knowing whether the objects it retrieves are Integer
s or something else, without checking every time.
However, generics allow types and methods to define one or more generic parameters. The compiler uses these to constrain the the types of parameters or return values that are allowed at a call site or within a method.
For instance, the aforementioned java.util.List
interface defines a single generic parameter, <T>
. Most of the methods in the interface make use of this generic parameter for their own arguments and/or return type.This small example shows how a generified type can be used:
⭐ Submission from dangerously_casual
Generic types are placeholders for reference types that can be used to make a piece of code work for multiple types, without having to duplicate the code. They're like C++'s templates, although less advanced. Whereas C++'s templates are literal templates that just insert the type (or value) used in the generic type parameter, Java figures out the types at compile time. The resulting bytecode then uses the first parent type applicable to all possible types of the generic type, and can safely cast that parent type to the used generic type based on usage.
this will be compiled to:
but due to compile time constraints, javac can safely transform usages of the code like
to
.
Doing it this way means that basic generic types are supported, but this also means that type information about a generic type can't be accessed at runtime, since the generic type doesn't really exist at runtime anymore. A hack is using an automatically filled vararg parameter with the generic type you want to test, such as:
because this method can be called like
this.<String>printTheGivenType()
, without passing the vararg parameter explicity, javac will generate an empty array of T, and pass it to the method, which looks like this at runtime: printTheGivenType(new String[0])
. With that information, we can figure out which type the array has, and can thus figure the type out.
This isn't exactly foolproof, since you can technically pass an array to the method manually and use some other type extending T, but it's the best method we have right now.Reified generics solve this problem, but they're not supported in java yet.
Submission from 0x150