1

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.

  • 3
    If you want to understand Generics in Java, you must understand reification and type erasure. Search for these, and you'll understand why this kind of thing isn't possible without warnings (and why arrays are now de facto a deprecated language element). It's all the result of very java-specific trade-offs that resulted in something quite different from C++ or C#. – Kilian Foth Jul 29 '15 at 10:20
0

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.

  • 1
    Giving Mike the answer, as he not only confirmed my suspicion that I would need to use the warning suppressor, but also read my mind in explaining how to minimize the surface area. – user3404036 Aug 9 '15 at 23:47
0

The only working solution is to suppress the warnings. That's what the existing generic collections also do.

So it doesn't mean there's anything wrong with your design - if there is anything wrong, it's with the design of Java generics (which had to make some compromises to achieve downwards compatibility).

0

It is a sort of catch 22, I agree. However if you know that only instances of type T are added to the list, then you can surpress the warning without repercussions. The compiler isn't intelligent enough to derive this information on its own.

However, also know that should you create another method that allows the insertion of instances of type ClassBase, the compiler will let you do it, and the warning that you surpressed would be warning you exactly of this type of problem and will cause a ClassCastException for types that are not T, so be mindful of this fact!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.