Casting between an object of generic type “T” and

2019-09-16 10:34发布

问题:

I've implemented an ArrayLinearList class on the following way

public class ArrayLinearList<T> implements LinearList<T>, Iterable<T>

which contains the class member

protected T[] element

In order to extend the functionality of the class I create a new class that inherits from the first one

class SuperList<T extends Comparable<T>> extends ArrayLinearList<T>

Here I ensure that the type T implements the Comparable interface (this cannot be changed).

In the SuperList class I have the following method

public void replace( int pedro, T maria ){
   element[ pedro ] = maria;

}

The line of the method generate the following error

Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Comparable;

But if I declare the class on the following way I don't have this problem anymore (but in this way I'm no longer able to compare class objects)

class SuperList<T> extends ArrayLinearList<T>{

I would appreciate any help to solve this issue. I'm not allowed to modify the class \ [ ArrayLinearList, just the class SuperList.

Thanks in advance.

回答1:

Based on the exception, it looks like you're initializing element with new Object[someLength] and doing an unchecked cast to T[]. This is likely happening in ArrayLinearList. The problem is that in this code:

element[ pedro ] = maria;

element is treated as a Comparable[] at runtime, and an Object[] isn't a Comparable[].

There are many existing posts related to generic array creation, but your issue is interesting because you've thrown inheritance into the mix (with a narrowing of a type parameter's bounds). The best solution seems to be only interacting with element within the ArrayLinearList class. Make it private and expose getters and setters. This should avoid runtime failures like this (which take some thought to avoid when doing unchecked casts like (T[])new Object[someLength]).

For example:

public class ArrayLinearList<T> implements LinearList<T>, Iterable<T> {

    private final T[] elements;

    public ArrayLinearList(final int length) {
        @SuppressWarnings("unchecked") // won't be exposed outside this class
        final T[] withNarrowedType = (T[])new Object[length];
        elements = withNarrowedType;
    }

    public final void set(final int index, final T element) {
        elements[index] = element;
    }
}

EDIT: I just noticed your disclaimer "I'm not allowed to modify the class ArrayLinearList just the class SuperList." Well that's unfortunate, because the author of ArrayLinearList has written type-unsafe code. But the question becomes why a method like SuperList.replace needs to interact with the array directly (or exist at all). It looks like it's simply assigning a new element to the specified index - is there no ArrayLinearList method you can use to do that?

If all else fails, here's a last-ditch workaround:

((Object[])element)[pedro] = maria;

But that's an awful solution that duct-tapes over the underlying problem.