0

I'm learning to work with generics, I can't understand why this is throwing an exception:

My 'GenericList' class:

package com.company.generics;

public class GenericList<T extends Number>{

    private T[] items = (T[]) new Object[10];
    private int count;

    public void add(T item){
        items[count++] = item;
    }

}

My main class:

package com.company;

import com.company.generics.GenericList;

public class Main {

    public static void main(String[] args) {

        GenericList<Integer> g = new GenericList<>();

    }
}

Exception thrown:

Exception in thread "main" java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [Ljava.lang.Number; ([Ljava.lang.Object; and [Ljava.lang.Number; are in module java.base of loader 'bootstrap')
    at com.company.generics.GenericList.<init>(GenericList.java:4)
    at com.company.Main.main(Main.java:9)
siris
  • 15
  • 2
  • Because you can't do it. You can't cast `Number[]` to `Integer[]`, for example. You have to cast the array *element* everywhere required. – user207421 Mar 14 '21 at 04:46
  • See https://stackoverflow.com/questions/18666710/why-are-arrays-covariant-but-generics-are-invariant – tgdavies Mar 14 '21 at 04:58
  • 1
    Does this answer your question? [Why "java.lang.ClassCastException: \[Ljava.lang.Object; cannot be cast to" error for Bounded type parameters and not for Formal Type parameters?](https://stackoverflow.com/questions/58036163/why-java-lang-classcastexception-ljava-lang-object-cannot-be-cast-to-error) – Savior Mar 14 '21 at 05:04

1 Answers1

2

Since T has an upper bound, the cast has an upper bound and is actually resolving to Number rather than Object. You'll have to declare your array of that type instead of Object.

private T[] items = (T[]) new Number[10];

And according to Effective Java, you may have to add a @SupressWarnings("unchecked") to that assignment. I didn't actually try it however.

markspace
  • 10,621
  • 3
  • 25
  • 39
  • A change from when? – M. Justin Mar 14 '21 at 04:53
  • 1
    Well I don't recall it working that way before. I've always just used `Object[]` like the op has. But I might be misremembering. – markspace Mar 14 '21 at 04:54
  • 1
    I feel like it's always resolved to the most specific type that can be inferred at compile time, though that doesn't come up too often. – M. Justin Mar 14 '21 at 04:55
  • Because the type is correctly declared as `T[]`, so you want to match it. It's safer to do it that way because a `return items[x]` will return the correct type. C.f. *Effective Java* by Joshua Bloch, Item 26 "Favor Generic Types." – markspace Mar 14 '21 at 05:01
  • I'm sure the OP's example has been reduced to the minimum for the sake of the question. :) I don't see any practical use for a class with only an `add()` method but no way to retrieve the values. – markspace Mar 14 '21 at 05:04
  • I'm not sure I follow. The OP's code does compile, it throws a runtime error. – markspace Mar 14 '21 at 05:07
  • Thanks for the answer, I thought that as the Integer class extends the Number class I could write it in the way in which I did. A tutorial explaining generics showed the exact code compiling and running without throwing an error. – siris Mar 15 '21 at 17:31
  • I've edited my answer to remove this part, but I don't remember needing to use `Number` instead of `Object` in these situations when generics were first introduced. Of course, it's possible I don't remember correctly, and the author of your tutorial didn't check their code. – markspace Mar 15 '21 at 23:07