As I was reading through GOOS, I came across a part of the code that uses generics, and a caution to try to not use generics whenever possible. This brought up one important question: what the heck is a generic?
According to the Java documentation, generics in Java allow types to be parameters when defining classes, interfaces and methods. By type we mean a class or interface which could be a given Java class like String
or Integer
, or a custom class or interface defined within the system.
Generics have a very distinct marking in the code. Here is an example of how an object would be created without using generics.
ArrayList carModels = new ArrayList();
An object that is created with generics will have a <Type>
sewn to the class of object being created.
ArrayList<String> carModels = new ArrayList<String>();
So what is the benefit? Generics is essentially a way of casting in where mismatched types can be detected on compile rather than at runtime. The generics only exist at compile time because the generics, as explicit type notations, are ignored during runtime by a process known as type erasure. Because the types must be considered for compiling, any issue with type matching will be handled up front.
Type casting can certainly be done during runtime, however if there is an issue, you will get a nasty runtime error, which is not preferable. Consider the following basic example where we are expecting to receive an integer value as a return.
public class SomeThing
{
private Object value;
public SomeThing(Object value)
{
this.value = value;
}
public Object getValue()
{
return this.value;
}
}
public class Main
{
public static Integer value1 = 10;
public static SomeThing thing = new SomeThing(value1)
public static void main(String[] args)
{
Integer int1 = (Integer)thing.getValue();
system.out.println(int1);
}
}
Since I have value1 assigned as an integer, and I am casting the value as an integer, this will work. But there is still potential for a problem. What if for some reason, the value at value1 was an integer, but we are trying to cast it as a string? Well, we would get a runtime error. It won’t complain before it is run because a String is still an object, which is the class that we are assigning to value in SomeThing.
In order to prevent this, we can change the SomeThing class to use generics.
public class SomeThing<V>
{
private V value;
public SomeThing(V value)
{
this.value = value;
}
public V getValue()
{
return this.value;
}
}
As you can see in the rewrite above, we have replaced Object in our SomeThing class with a type, V, that we are declaring on the class. In order to use this class now, we have to make a slight modification to our declaration.
public class Main
{
public static Integer value2 = 10;
public static SomeThing<Integer> thing = new SomeThing<Integer>(value2)
public static void main(String[] args)
{
Integer int2 = thing.getValue();
system.out.println(int2);
}
}
You will notice a couple of changes here. First, you don’t have to type cast where we are creating int2. Second, we are strongly declaring that SomeThing’s V attribute is of type Integer. Anywhere in SomeThing where there is a V, you can read that as Integer.
Now, if you say that int2 is of type String instead of Integer, you will get a compiling error.