Lesson 1 - Introduction to collections and genericity in Java
In today's lesson of our Java course, we'll introduce collections and go over a bit of background information about them. Then, we'll discuss different build-in collections in more detail in the following lessons.
Collections
The term "collection" refers to a set of items which are mostly of the same
data type, and used for a specific purpose. Throughout the courses, we've
already encountered two types of collections, i.e. arrays and
ArrayLists
. There are a lot of collections and although they often
look similar from the outside, internally, they work in very different ways, and
we choose them according to our intents and purposes. Java provides a large
amount of pre-made collections. We'll gradually introduce you to them and work
with them as needed.
Generic and non-generic collections
If we thought about making our own collection, we'd run into issues very
soon. One of which would be choosing the data type for the collection items. For
example, if we wanted to program our own ArrayList
, we'd create a
MyList.java
class and add the appropriate methods to it. Since we
want to make our collection universal and to be able to store any sort of items,
e.g. both int
s or User
objects, we'd run into issues
with the data types of the elements within the collection. There are two ways to
solve this problem and Java itself contains collections using both of these
approaches to make its collections universal.
Non-generic collections
Since we know that all data types are descendants of the Object
class, we can use this data type to store elements of our collection. We'd then
be able to put anything into our collection. The downside to this approach is
that the collection itself won't know the actual type for each of the items.
Therefore, it'd only be able to return these elements simply as general objects.
Meaning that we would have to cast anything returned by the collection.
Here's an example of a non-generic collection, without the generic data type:
ArrayList list = new ArrayList(); list.Add("item"); String item = (String)list.get(0);
We create a list and add an item of the String
type. In order to
retrieve this item back from the list, we need to re-cast it back to a
String
.
To make the code above work, we have to add
import java.util.ArrayList;
.
Generic collections
Generic collections solve the data-type problem at the Java language-level.
They introduce genericity. Simply speaking, it's the ability to specify the data
type at the moment when an instance is created. In the collection class, we work
with a generic type which serves as a placeholder for future data types. You may
see it as a data type of a class, that changes into others when an instance is
created, for example, a String
. It's sort of a class
parametrization.
We already know the generic ArrayList
and that the data type
(parameter) of generic classes is defined inside the angle brackets. We only
have the ability to specify the data type once, when we create the collection.
This way, we no longer need to cast items:
ArrayList<String> list = new ArrayList<String>(); list.add("item"); String item = list.get(0);
The program works the same as the one with the non-generic
ArrayList
, but we can now read values without using casts.
Generic collections have replaced non-generic collections which are not used often nowadays. In this course, we'll focus mainly on generic collections and only mention their non-generic versions.
Genericity
Genericity is, of course, a feature of the Java language, so we have the privilege to use it in our classes.
At this point, we won't bother with creating our own collection. Instead,
let's create a simple class that will manage a single variable. The variable
will be generic, so we'll be able to store any data type in it. Create a new
console application project and name it Genericity
. We'll add a new
class and name it MyClass
. Let's add the generic parameter in its
declaration and name it T
:
public class MyClass<T> { }
We are able to enter multiple generic parameters into the angle brackets, separated by commas. This may be useful at times, we'll go further into things like this once we get to generic maps.
Let's move to the main()
method and create an instance of our
class:
MyClass<Integer> instance = new MyClass<>();
Don't forget to provide the angle brackets in both the data type and the
constructor. In Java 7 and newer, there's no need to add the data type to the
constructor. We've specified the int
data type in the
T
parameter for this class instance. We could also make another
instance of the same class and give it a totally different data type, e.g.
String
. So we just need 1 class for storing more data types.
Let's continue and create a class field. We are able to use T
as
an ordinary data type. We'll also add a constructor to the class, which will
initialize the variable:
public class MyClass<T> { private T variable; public MyClass(T variable) { this.variable = variable; } }
Make sure to update the instance creation in the main()
method:
MyClass<Integer> instance = new MyClass<>(10);
The instance contains the variable
field now, which is of the
int
type and contains a value of 10
.
We could even add a method with an extra generic parameter (other than the one that our class currently has). It could look for example like this:
public <T2> boolean compare(T2 a) { return variable.equals(a); }
Let's compare our Integer
with another data type:
instance.compare("15");
Java can determine the generic type from the parameter
Other constructs
For completeness' sake, we'll introduce you to a few more constructs.
A generic class parameter can be specified in more detail, furthermore, it
can be limited using the extends
keyword. For example, we can
ensure that the data type implements the Comparable
interface:
public class MyClass<T extends Comparable> { ... }
This allows us to call the interface's methods on the variables of the
T
type from within the class. The interface itself can also contain
a generic parameter, so we could use generic types in its methods' headers.
To top it all off, let's go over how to limit the parameter type in terms of inheritance.
public class MyClass<A extends B, B extends C, C> { }
In the example above, we declare a class with three generic parameters where
A
is a descendant of B
and B
is a
descendant of C
.
In the next lesson, Java Collections Framework, we'll look at List
s, and introduce
the different implementations of this collection as well as their advantages and
disadvantages.
Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.
Download
By downloading the following file, you agree to the license terms
Downloaded 6x (20.47 kB)
Application includes source codes in language Java