Java Generics: Why does my @SuppressWarnings("unchecked") work in this case, but not the other?

StackOverflow https://stackoverflow.com/questions/16879680

  •  30-05-2022
  •  | 
  •  

Pergunta

Question is similar to this Why do we have to use an intermediary variable for @SuppressWarnings("unchecked")?, but I couldn't glean a solution from that one.

As an exercise, I'm building a hash-table construct from scratch. So I wrote a class LinkedListDemo auxiliary to HashTableDemo; the LinkedListDemo class tests absolutely fine. Particularly, the following subroutine in causes no ClassCastException errors at run-time:

    public LinkedListDemo<S, T> reverse() {

        if (count == 0)
            return null;

        @SuppressWarnings("unchecked")
        S[] keys = (S[]) new Object[count];
        @SuppressWarnings("unchecked")
        T[] vals = (T[]) new Object[count];

        ListNode<S, T> runner = head;
        for (int k = 0; k < count; k++) {

            keys[k] = runner.pair.getKey();
            vals[k] = runner.pair.getValue();

            runner = runner.next;

        }

        ArrayUtils.reverse(keys);
        ArrayUtils.reverse(vals);

        return new LinkedListDemo<S, T>(keys, vals);

    }

Whereas the following, in my HashTable class does:

public class HashTableDemo<S, T> {

    private LinkedListDemo<S, T>[] table = (LinkedListDemo<S, T>[]) new Object[10]; 

// more code...

}

Does anyone know how Java's HashMap class circumvents this issue, and/or how I can? I tried creating an intermediate variable within a constructor as suggested in the above link - but it didn't work.

Foi útil?

Solução

This will compile:

@SuppressWarnings("unchecked") // this is okay because [insert reasoning here]
private LinkedListDemo<S, T>[] table =
        (LinkedListDemo<S, T>[]) new LinkedListDemo<?, ?>[10];

The reason your code errors is because an Object[] is not a LinkedListDemo[], similarly to how an Object is not a LinkedListDemo.

The earlier unchecked casts work because S[] and T[] have been erased to Object[] at runtime.

In general, arrays of parameterized types are discouraged because their very existence is unsafe. This is because:

  • Arrays are covariant - a LinkedListDemo<S, T>[] is an Object[]. So one could assign table to an Object[] variable and then assign a String as an element - this would be legal at compile time but would at least fail at runtime with an ArrayStoreException, since arrays do runtime type checking.
  • Generic type information is not available at runtime because it's been erased by the compiler. One could once again assign table to an Object[] variable but this time assign a LinkedListDemo<String, Integer> as an element. At runtime, the array would only see that a plain LinkedListDemo had been added, so it would not fail right away if the generic types were wrong. This might lead to unexpected ClassCastExceptions at some later time.

See my answer here for a fuller explanation and example of this issue. See also this great explanation by Angelika Langer's Generics FAQ: Can I create an array whose component type is a concrete parameterized type?

The takeaway is that it will be much easier and safer to simply use a List<LinkedListDemo<S, T>> instead. If you ultimately decide to stick to using LinkedListDemo<S, T>[], make sure that it is carefully hidden as an implementation detail and stay cognizant about how it can be misused.

A couple of other misc notes:

  • @SuppressWarnings("unchecked") doesn't do anything but suppress the unchecked cast compiler warning. It's essentially saying "trust me I know what I'm doing" - but you still need to be careful that an unchecked cast isn't being misused. See this post for more info: How do I address unchecked cast warnings?
  • Like all core API classes, the source for HashMap is available so you should feel free to look over it if you want to understand its inner workings better.
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top