Java Generics Tutorial – J053
DeegeU Java Course
The “Java Generics Tutorial” video is part of a larger free online class called “Free Java Course Online”. You can find more information about this class on “Free Java Course Online” syllabus.
Java Generics
At some point you’ve likely heard about Java generics. Generics were introduced into the Java language in Java 5. In the next few videos, we’re going to take a deep dive into what generics are, how to use them, and ultimately how to create our own Java generics.
The idea is this. We have a class we want to use for multiple types. We might have a list that stores numbers, a list that stores Strings, a list that stores sprites, or any other specific class. We want to write this list class once, and use it for all object types.
What is it we’re trying to do?
Let’s start with a hypothetical video game. In the game we’ll have a list of sprites we want to draw on the screen. Our code updates the positions of the sprites in each game loop, and then we draw the sprites.
So far if we wanted a list of instances, we needed to use an array. We’d define our sprite, and add it to the array by position. This gets a bit tricky, because we need to know the number of sprites in advance when we declare our array. Or limit it to a set upper bound number, and keep track of the number in the array. There’s no clear way to add or remove sprites either. For example if we deleted a sprite from the middle of our array. We’d have to constantly shift the array to delete sprites, or we could add a flag stating if the sprite was active. This is all do-able, but it’s not clean.
We could create a linked list class to manage our sprites, and create methods for adding and removing sprites. This would solve some of the problems, but not all. What happens when we have another list with similar requirements, but it’s a list of something else. Like an inventory list for our character. Completely different objects in the list, but we still need add and remove methods.
So we could create another class with almost exactly the same code, but this time it uses inventory objects instead of sprites. That sucks. When we repeat code, that’s a sign we’re doing something wrong.
Another option might be to make the class hold only instances of objects. Then we can reuse the class for anything. The problem with that is we now have to cast our objects back to the class we want in order to use it. That’s too much casting back and forth, and makes very ugly code.
What happens if we accidentally add an inventory object to our sprite list?
Code goes boom, and we won’t know it until we run the program. And we won’t know where the incorrect add happens. We’ll add the object successfully, and then we’ll crash way later when we try to use the wrong object.
The problem here is we don’t have type safety. We can’t guarantee the types entering our list, or coming back out. We’re casting and praying. Not good. This was the solution before Java 5.
Luckily Java provides a large group of collection classes that solve all our problems. We’ll go over those classes in depth in another tutorial, but right now we’ll stick with the ArrayList class. The ArrayList class is a collection class that holds a dynamic array of some object. What object you ask? Well we need to learn a bit about generics before we can go into detail about the collection classes.
What is a Java generic type?
So what is a generic? A generic is a class that acts like a template for creating classes. In this case, we define how the list works without stating what classes the list holds. We’ll have add and remove methods, but we won’t say what class we’re adding and removing until we define the instance. Once we instantiate our class, we’ll tell it the type our list works with. No other type is allowed. Using a generic class gives us type safety.
What appears to happen behind the scenes is Java will take our template, fill in the desired class we want, and instantiate the class using the type we pass to it. That’s why you’ll sometimes hear generics referred to as templates. I do want to be clear however, Java is not using templates like C++. In reality, Java’s just performing type checking for you.
Invoking and Instantiating a Java Generic Type
The code we saw before Java 5 looked like this. We had an ArrayList which we could add any class instance. We add calling the add method, and when we get things back out we get an instance typed as an Object.
To use the class we’d cast it back to the class we need. Going back to our sprite example here’s a list, but we don’t know what kind of list.
ArrayList list = new ArrayList();
Cast objects inside our list to the wrong type, or put in the incorrect instance type and things go boom at runtime. We need to tell Java what our ArrayList holds so we can perform checks at compile time.
We define our ArrayList like this.
ArrayList sprites = new ArrayList();
Notice we’re specifying the type our ArrayList holds inside the angle brackets. This means when we go to add Sprites to the list using the add method, the add method will only accept Sprites. We can’t add an inventory object to this ArrayList. This ArrayList only holds Sprites. The right hand side of the statement instantiates our ArrayList, so we need the parenthesis like we would for any class instantiation.
This is now an ArrayList that works only with Sprites. If we tried to add a String instance to the list, it would not compile.
You might have noticed, we’re telling Java this ArrayList holds Sprites in the type declaration, and then we’re telling Java again our ArrayList holds Sprites in the class instantiation. That’s annoying, and not necessary. Java can figure it out. We can rewrite the instantiation like this.
ArrayList sprites = new ArrayList<>();
The angle brackets are referred to as the diamond, and Java figures out the class type through “type inference”. That’s just a fancy way of saying, Java can figure out the type from the left hand side.
You’re probably thinking, hey do we need the diamond at all? Java can tell from the left hand side we’re using a generic. What happens when we do this?
ArrayList sprites = new ArrayList();
We will get a warning saying the Java file “uses unchecked or unsafe operations”. Not a helpful warning message. What’s happening here is Java used to have a class called ArrayList before they introduced generics in Java 5. To make things backward compatible, they introduced something called raw types.
Java Raw Types
A raw type is the class as if generics were never introduced. Since the ArrayList predates generics, the raw type is the class pre-Java 5. In the case of our ArrayList we could do something like this.
ArrayList sprites = new ArrayList(); sprites.add(sprite1); sprites.add(sprite2); sprites.add("some string");
We are adding a string to our list.
The class would behave as if we could store any object in the ArrayList. Not good. We’d be back to where we were before. Now if we add the type and the diamond, things work as we’d expect. Adding a String makes things break.
ArrayList sprites = new ArrayList<>(); sprites.add(sprite1); sprites.add(sprite2); sprites.add("some string"); // COMPILE ERROR!!
Because of this, you never want to use raw types. There’s no reason to. It’s there for backward compatibility to old versions of Java. However, we do want to make sure we use the diamond so Java knows we’re working with the parameterized type and not the raw type.
Raw vs. Parameterized
Next you’re likely wondering, what’s the difference between a raw type and the parameterized type? What I mean is, what the difference between the raw type, and passing Object as the class in our list like this.
ArrayList rawList = new ArrayList(); ArrayList<object width="300" height="150"> objectList = new ArrayList<>();
Well rawList doesn’t have the generic compile time type checking, while objectList explicitly says we accept any object that derives from the Object class. That might seem like a slight difference, but let’s look at it another way.If we add another list like this:
ArrayList<sprite> spriteList = new ArrayList<>();
And then we have a method in another class for drawing our sprites that accepts a list called draw(). If we define draw() like this
void draw(ArrayList theList) {...}
the draw method will accept any of the lists we’ve created. It will treat any list we pass as a raw type, which is a list of objects.However, if we define it like this
void draw(ArrayList<object> theList) {...}
the draw method will only compile with objectList and rawList. It won’t compile with the spriteList.And for completeness, if we create a draw method that expects only sprite lists like this
void draw(ArrayList<sprite> theList) {...}
The method will work only with the raw list and the sprite list. The lesson here is an object list is a different type from the sprite list, even though sprites are child classes from objects. Inheritance is not supported in generic type parameters.Here’s a weird bit. If we print out the class type for each list like this,
System.out.println("Rawlist class is " + rawList.getClass()); System.out.println("ObjectList class is " + objectList.getClass()); System.out.println("SpriteList class is " + spriteList.getClass());
it will print this out:
Rawlist class is class java.util.ArrayList
ObjectList class is class java.util.ArrayList
SpriteList class is class java.util.ArrayList
Everything is an ArrayList. We can even test the instances to see if the class types are equal
System.out.println("SpriteList class is equal to RawList: " + (spriteList.getClass() == rawList.getClass())); System.out.println("SpriteList class is equal to ObjectList: " + (spriteList.getClass() == objectList.getClass()));
which all print out as true.
SpriteList class is equal to RawList: true
SpriteList class is equal to ObjectList: true
That means you can do weird things like this.
ArrayList<sprite> spriteList = new ArrayList<>(); ArrayList rawList = spriteList;rawList.add("Test"); Sprite spriteOne = spriteList.get(0); // RUNTIME EXCEPTION
We can create a sprite list, cast it as a raw list, add a string, and things won’t blow up until we run it. However if we try to cast the sprite list to an array list that holds objects, it won’t even compile.
ArrayList<object> objectList = (ArrayList<object>) spriteList; // THIS WON'T COMPILE
What’s happening behind the scenes?
So what’s happening here? I kinda alluded to this when I called generics templates, but in Java they really aren’t templates. There’s two ways you can do generics. The first way is the compiler creates a new version of the class for every type we wish to create. This is code specialization, and a true template. The compiler would be creating a specializaion of every class we wish to create. Our code would generate and compile code for our sprite array list, and generate more code for our object list, and even more code for any other array list type we want to create. This can lead to really large program sizes.The other way is through code sharing. Basically the compiler creates one version of our array list. In order to do this, Java uses the raw types and then handles all the casting for us. It’s much smaller code, since there’s only one ArrayList class compiled, but it can make things interesting. Let’s look at what that means.Let’s start with the code we were using for sprites.
ArrayList<sprite> spriteList = new ArrayList<>(); spriteList.add(new Sprite()); Sprite aSprite = spriteList.get(0);
This is what Java replaces it with behind the scenes.
ArrayList spriteList = new ArrayList(); spriteList.add(new Sprite()); Sprite aSprite = (Sprite) spriteList.get(0);
That’s why when we compared the class types of the different instances, they all came out the same. Notice the array list is converted to the raw type, and a cast is added when we get the sprite out of the list. The compiler is only compiling one ArrayList class. It’s the one just handling objects.At compile time the casts are added where they are required, but there are no new ArrayList types created by the compiler. The code is shared for all types passed to the ArrayList. Java is adding casts for you behind the scenes. This cast can be added at compile time, because Java knows what is going in and out of the list at compile time. Runtime is a different story. That’s why when we look at the class type of the instances, we get ArrayList for all of the instances. The type contained by the ArrayList is erased, and it requires reflection tricks to get the type back out. We’ll look at type erasure more in the coming tutorials.
Java Generics Wrap-up
So that’s how generics work in Java. We looked at what generics were, how to instantiate them, what are raw types, and finally how Java treats generics behind the scenes. The important takeaways are Java generics allow us to reuse classes with different types without sacrificing type safety, and behind the scenes there is still only one class representing our generic class.If you have any questions, add them to the comments below. If you got this far, hopefully you liked the video. If so, make sure you like, share and subscribe!And with that, I’ll see you in the next tutorial!
<!-- DeegeU - Right Side -->
<ins class="adsbygoogle" style="display:inline-block;width:336px;height:280px" data-ad-client="ca-pub-5305511207032009" data-ad-slot="5596823779"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script></p>
Tools Used
- Java
- NetBeans
Media Credits
All media created and owned by DJ Spiess unless listed below.
- No infringement intended
Airport Lounge – Disco Ultralounge by Kevin MacLeod is licensed under a Creative Commons Attribution license (https://creativecommons.org/licenses/by/4.0/)
Source: http://incompetech.com/music/royalty-free/index.html?isrc=USUAN1100806
Artist: http://incompetech.com/
Get the code
The source code for “Are you ready to tackle the fizzbuzz test in Java?” can be found on Github. If you have Git installed on your system, you can clone the repository by issuing the following command:
git clone https://github.com/deege/deegeu-java-intro.git
Go to the Support > Getting the Code page for more help.
If you find any errors in the code, feel free to let me know or issue a pull request in Git.
Comments
DJ Spiess
Your personal instructor
My name is DJ Spiess and I'm a developer with a Masters degree in Computer Science working in Colorado, USA. I primarily work with Java server applications. I started programming as a kid in the 1980s, and I've programmed professionally since 1996. My main focus are REST APIs, large-scale data, and mobile development. The last six years I've worked on large National Science Foundation projects. You can read more about my development experience on my LinkedIn account.