Generic Types
Sometimes, we want things to support any type, including user defined types that we don't know about! For example, it would make sense that we don't care what type we make a List
out of, since it's just a whole bunch of objects put together.
The Java solution is generics! Generic types are denoted by a <>
and can be appended to methods and classes. Here's an example with classes:
In this example, SomeType
is a Generic Type Variable that is not a real type, but can still be used inside the class as normal.
On the other hand, String
is an Actual Type Argument that replaces SomeType
during runtime. Now, every time SomeType
is used in SomeClass
Java treats it exactly like a String
.
Generic Subtypes
Like in **Dynamic Method Selection, adding inheritance makes things tricky! Let's look at an example:
Will line 3 error?
Will line 4 error?
Will line 5 error?
Arrays have slightly different behavior than this and will throw an ArrayStoreException
if types are mismatched in any way.
Type Bounds
Sometimes, we want to put constraints on what kinds of types can be passed into a generic type.
One way of doing is is to specify that a generic type must fit within a type bound: here, T must be some subtype of a specified type Number
.
We can also do it the other way and specify that a type can be a supertype of a specified type. Both of these examples are shown below:
Limitations of Generic Types
The biggest limitation is that primitive types cannot be used as generic types. For example, List<int>
is invalid and will not work!
One workaround to this is to use the reference-type counterparts to primitives, such as Integer
, Boolean
, Character
and so on. However, converting between these types and primitive types, which is called autoboxing, has significant performance penalties that must be taken into consideration.
Another limitation is that instanceof does not work properly with generic types. For instance, new List<X>() instanceof List<Y>
will always be true regardless of what types X and Y are.
Last updated