Java – Implementing a Generic List

java

In an effort to understand Generics in Java (and how to use them), I am trying to implement a list structure using them but have hit a wall.

Scenario:

Suppose there is some classes (we'll call them ClassA, ClassB, and ClassC) which all extend ClassBase. ClassBase is abstract, and simply contains an ID code with appropriate getters and setters. The subclasses add additional attributes, but those aren't important.

The list is designed to be able to hold a number of the above sub-classes, and manipulate the list using the common ID attribute. The list needs to be growable, and each list will only contain one type of sub-class (so a list of ClassA, ClassB, or ClassC. No mixed combinations of those).

Attempt 1:

I set up my List class like so:

ClassList<T extends ClassBase>

I then declare an array of elements as "private T list[]", and write the remainder of the class. The signatures for the element getters and setters are as follows, since those might be important:

public T get(int index)
public void set(int index, T item)

Everything appears to be good, except when I go to compile it complains about every instance where I try to recreate the underlying array (in order to shrink/grow it).

.\test\ListClass.java:10: error: generic array creation
        list = new T[0];
               ^

So after some thought and reading the error makes sense, as the compiler doesn't know what constructors are going to be available (at least that's how I understand it), so it doesn't appear that this way will work.

Attempt 2:

I keep the same class declaration, but change the internal array to "private ClassBase list[]", and then adapt my code accordingly. The main change is casting from ClassBase to T and back when necessary. So the getter for instance looks like this:

public T get(int index)
{
    return (T)(list[index]);
}

So now my code compiles and executes, but every instance where I have done the above casting now throws the infamous unchecked warning:

.\test\ListClass.java:16: warning: [unchecked] unchecked cast
        return (T)(list[index]);
                  ^

I can see why the warning is appearing, but at the same time the ListClass controls all means of accessing and manipulating the list, so I can guarantee that only elements of type T are placed in there.

Problem:

I suppose I could suppress the warnings. But I am of the belief that they are there for a reason and would prefer to get rid of them the proper way. The problem is I can't work out what the 'proper' way is (or whether infact suppressing the error is the proper way in this case).

So that's my question. What is wrong with my design, and how do I fix it?

Thanks in advance.

Note:

Before anyone tells me to use one of the existing generics and stop reinventing the wheel, that's not the point. My aim isn't to recreate a replacement for the generic lists, it is to understand generics, and a list like above is about the best example I can think of which clearly shows how they work.

Best Answer

The unchecked cast warning is one of the most problematic, annoying, and ultimately useless warnings issued by java. I say "ultimately useless" instead of just plain "useless" because it is not entirely without merit, it is good to know that you have an iffy conversion somewhere, but it just so turns out that if you do any work whatsoever with generics in java, you will want to add @SuppressWarnings("unchecked") as a macro to your keyboard, that's how often you will have to be suppressing this warning.

Essentially, the value of the unchecked warning is that all your iffy conversions will be easily identifiable in your source code because of the @SuppressWarnings("unchecked") statements attached to them.

So, there is nothing wrong with suppressing the unchecked warnings, as long as you know what you are doing. If you later come across a class cast exception at runtime, you will know that the cause of the problem is probably in the nearest @SuppressWarnings("unchecked") statement.

Just be sure whenever possible to suppress unchecked warnings at the statement level, instead of at the function or at the class level. For some weird reason, java will not allow this:

@SuppressWarnings("unchecked")
return (T)x;

so this might tempt you to suppress the warning at the function level and be done with it. However, this will work:

@SuppressWarnings("unchecked")
T temp = (T)x;
return temp;

This way, the warning is localized to the exact statement that contains the iffy conversion, so you know precisely where every single iffy conversion is. If you suppress at the function or at the class level, then iffy conversions could be anywhere, you would not know.

That having been said, another very good option that you have is to abandon the idea of creating and manipulating your own array of generic objects, and have your home-grown list class contain Java's ArrayList<T> instead. This class exists precisely in order to encapsulate generic arrays, and as Michael Borgwardt has already stated, if you look at its source code, you will see that it contains an array of java.lang.Object, and that it is choke full of @SuppressWarnings("unchecked") clauses. The benefit of using it is that it has been thoroughly tested, so all these conversions are known to be good, so you can't go wrong with it.

Related Topic