0% found this document useful (0 votes)
16 views

Generic Java

Generics allow for type-safe operations on objects of unknown types. They enable the creation of classes and methods that can work with many different types instead of a specific one. This is done through type parameters that represent unknown types and ensure type safety while retaining flexibility.

Uploaded by

connectpatriot
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
16 views

Generic Java

Generics allow for type-safe operations on objects of unknown types. They enable the creation of classes and methods that can work with many different types instead of a specific one. This is done through type parameters that represent unknown types and ensure type safety while retaining flexibility.

Uploaded by

connectpatriot
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 7

Generics

When Types Aren't Important


We've seen generics brie�y in the ArrayList and LinkedList lessons, but it's worth taking a
moment to consider the motivation and hence usage of generics.

Generics are the Java name for classes or methods that take type parameters. These are a bit like
parameters to a method, except they only carry type information, not values the way variables do.

The motivation for this is that we would like our classes and methods to be type-safe (be able to
detect when the wrong sort of information is passed to them), but often the functionality of the
method or class does not depend on the actual details of the type passed to them.

Taking the example that we have already seen, the ArrayList<E> class doesn't need to know
anything about the structure of the data it stores, but we do need to be able to check that it all has the
same type. So at run-time we need to be able to give E a value, and use it to check things, but we
don't need to know what it is to write the code to add something to the list.

This parameterisation by types is called type polymorphism and is one expression of the principle of
abstraction - we're abstracting away the internal details of the type, and thus allowing our generic
class or method to not have to be rede�ned every time we want to make it work with a new type.
A Motivating Example
Say we want to make a Box to store things in. A Box doesn't need to know anything about what's
stored in it, but Java is a strongly statically typed language, so we need to assign the contents a type.

There are two ways of doing this.

The �rst is to create a new Box for each type:

public class StringBox {

private String data;

public StringBox() {
data = null;
}

public String getData() {


return this.data;
}

public void setData(String data) {


this.data = data;
}

public boolean isEmpty() {


return data == null;
}

If we only ever want to Box Strings , then we're happy. If we need to Box something else, we have
to go and copy all this code to another class, and if we want a Box for every possible class we might
use, we're probably going to have a bad time.

The other approach is to make the Box as general as possible. Thanks to Java's class hierarchy, this is
easy (and before generics were added, it was the only sensible way):

public class Box {

private Object data;

public Box() {
this.data = null;
}

public Object getData() {


return this.data;
}

public void setData(Object data) {


this.data = data;
}

public boolean isEmpty() {


return this.data == null;
}

This Box can handle anything, but we lose type information, so we have to manually cast things back
to their correct type when we retrieve them from the Box (which means we have to remember or
�gure out the type, and is consequently more error prone than getting the right thing back to begin
with).
A Better Box
The solution to this is to add a type parameter to Box . This will allow Box to handle any type of data,
but will guarantee that the type is what you expect.

public class Box<T> {

private T data;

public Box() {
this.data = null;
}

public T getData() {
return this.data;
}

public void setData(T data) {


this.data = data;
}

public void isEmpty() {


return this.data == null;
}
}

Now if we create a Box , we are obliged to tell it what the type of the thing it's going to store is, and
the compiler will only allow that type of thing to put into it, and will make sure that's the type of stu�
that comes out of it.
The Syntax of Generics and Type Parameters
Specifying a type parameter is pretty simple. For a class, you just append <[name]> to the class
name, where you replace [name] with whatever you want the type parameter to be called inside that
class (usually beginning with an uppercase letter, because it will be substituted by a class or interface
name when used).

There is also no limit on the number of type parameters, instead of a single name, you simply
provide a comma separated list of names:

public class TwinBox<Type1, Type2> {...}

If you want a method to have a type parameter (that's not provided by the containing class), the type
parameter list goes before the return type:

public <T> ArrayList<T> listify(T[] arrayOfTs) { ... }


Using Generics and Type Parameters
When using a generic class we simply replace the type parameter with the type value we want to use
in that case:

ArrayList<String> listOfStrings = new ArrayList<String>();

For most generic methods, the type can be inferred, so you don't need to specify:

public <Type> ArrayList<Type> listify(Type[] arrayOfType) { ... }


...
//Somewhere else
ArrayList<String> listOfStrings = listify(stringArray);

But we won't be making methods with their own type parameters in this subject, so we'll leave that
there.

The major restriction on type parameters is the cannot be instantiated with primitives (as seen in the
ArrayList lesson). So the following is bad:

ArrayList<char> list = ...

Each primitive type has a wrapper class associated with it. This list is given in the ArrayList lesson,
so I won't repeat it here.
How Do Generics Work in Java?
This part is beyond what we'll need to know for the subject, so this is just for interest and edi�cation.

Generics are implemented in Java (mostly because they were added to the language relatively late) by
type erasure. This means that all the type checking is done at compile time (which is �ne, as Java is
strongly statically typed - all the type information must be known at compile time for the compilation
to work), so the type safety is guaranteed, but the compiled code just replaces the parameterised
version of the class with the raw, unparameterised version that uses Object wherever there was a
type parameter.

So our Box<T> compiles down to a Box . This means that the type information is not (readily)
available at run time. This causes some problems, such as never being able to create new instances of
a generic parameter type:

T newT = new T(); //This is disallowed


T[] arrayOfTs = new T[5]; //So is this

A couple of other side e�ects of this approach is that static variables and methods can't have a
generic type, and you can't make generic exception classes.

A positive side e�ect is that Java only needs to compile one version of the code, which is then used at
run time, in contrast to the C++ approach, which recompiles the code for every choice of instantiation
of the type parameters (and plays merry hell with header �le/code �le separations). Functional
languages generally handle this type of polymorphism more naturally.

You might also like