A Generics example

Filed under: Java, — Tags: Generics, JUnit, TDD, Test driven development — Thomas Sundberg — 2011-05-22

Generics has been a a part of Java since Java 5. It will allow you to write code that handles types without deciding which type upfront. This is of course very useful for collections. It used to be done using the inheritance system and casting Object to the type you knew should be used.

Using Object works, but the objects that are being moved around must be casted to the correct type before they can be used. Casting is done runtime. It means that there are always a risk for a ClassCastException. The risk is reduced when you use Generics since the check is done at compile time.

The risk for ClassCastException is of course very small if you always test drive your code.

I decided that I should create a Stack so I could explore the Generics in Java a little bit more.

The implementation is as follows.

A String implementation

Let's start with a simple case where I define a Stack that only handles String content. The tests for it looks like this:

package se.sigma.educational;

import org.junit.Test;

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;

public class StackTest {

    @Test
    public void pushAndTop() {
        Stack stack = new Stack();
        String item = "My test item";

        stack.push(item);

        assertThat(stack.top(), is(item));
    }

    @Test
    public void pop() {
        Stack stack = new Stack();
        String item = "My test item";

        stack.push(item);

        assertThat(stack.pop(), is(item));
        assertThat(stack.isEmpty(), is(true));
    }
}

It's really not much, I create a stack and add a string to it. I verify that the string is there. Then I add another string and verifies that it can be removed.

The String stack implementation looks like this:

package se.sigma.educational;

import java.util.LinkedList;
import java.util.List;

public class Stack {
    private List<String> content;

    public Stack() {
        content = new LinkedList<String>();
    }

    public void push(String item) {
        content.add(item);
    }

    public String top() {
        return content.get(0);
    }

    public String pop() {
        return content.remove(0);
    }

    public boolean isEmpty() {
        return content.isEmpty();
    }
}

I use a LinkedList to store the content. I define that the list should only contain Strings. I return String from top and pop.

This Stack can only handle items with the type String. It doesn't handle any corner cases. It will be ugly if I pop an empty stack.

A generic implementation

The test for a generic implementation may look like this:

package se.sigma.educational;

import org.junit.Test;

import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;

public class StackTest {

    @Test
    public void pushAndTop() {
        Stack<String> stack = new Stack<String>();
        String item = "My test item";

        stack.push(item);

        assertThat(stack.top(), is(item));
    }

    @Test
    public void pop() {
        Stack<String> stack = new Stack<String>();
        String item = "My test item";

        stack.push(item);

        assertThat(stack.pop(), is(item));
        assertThat(stack.isEmpty(), is(true));
    }
}

I create the Stack and I define that it should hold String objects. That is the only difference between the test for the string implementation and the test for the generic implementation.

The generic implementation looks like:

package se.sigma.educational;

import java.util.LinkedList;
import java.util.List;

public class Stack<T> {
    private List<T> content;

    public Stack() {
        content = new LinkedList<T>();
    }

    public void push(T item) {
        content.add(item);
    }

    public T top() {
        return content.get(0);
    }

    public T pop() {
        return content.remove(0);
    }

    public boolean isEmpty() {
        return content.isEmpty();
    }
}

The largest difference between the string implementation and the generic implementation is that the generic implementation defines a type T that will be used everywhere instead of String as in the previous example.

T is only a symbol and can be changed to anything else. If you like, you could call it type instead.

Conclusion

Using Generics isn't really difficult. Replace the type you use with any symbol, I use T in this example. Make sure that the calling code defines the type that should be used.

That's it folks!

Resources



(less...)

Pages

About
Events
Why

Categories

Agile
Automation
BDD
Clean code
Continuous delivery
Continuous deployment
Continuous integration
Cucumber
Culture
Design
DevOps
Executable specification
Git
Gradle
Guice
J2EE
JUnit
Java
Javascript
Kubernetes
Linux
Load testing
Maven
Mockito
New developers
Pair programming
PicoContainer
Presentation
Programming
Public speaking
Quality
React
Recruiting
Requirements
Scala
Selenium
Software craftsmanship
Software development
Spring
TDD
Teaching
Technical debt
Test automation
Tools
Web
Windows
eXtreme Programming

Authors

Thomas Sundberg
Adrian Bolboaca

Archives

Meta

rss RSS