7

Strange situation - below is the code:

ArrayList<String[]> listArr = new ArrayList<>();
Object[] obj = new Object[]{"str", listArr};

String str = (String) obj[0];//OK
ArrayList<String[]> list = (ArrayList<String[]>) obj[1];//warning: [unchecked] unchecked cast

When project is built (with compiler option -Xlint:unchecked in project properties), I get one warning:

warning: [unchecked] unchecked cast
ArrayList list = (ArrayList) obj[1];
required: ArrayList
found: Object

But casting String in the same way is OK. What is the problem here?

Stuart Marks
  • 127,867
  • 37
  • 205
  • 259
Ernestas Gruodis
  • 8,567
  • 14
  • 55
  • 117

3 Answers3

6

This is because the compiler can not verify the internal types at the list level, so you need to first verify for list. And the internal types individually.

Instead of ArrayList<String[]> list = (ArrayList<String[]>) obj[1];

It should be ArrayList<?> list = (ArrayList<?>) obj[1];

shikjohari
  • 2,278
  • 11
  • 23
  • 1
    OK, but how later change it to `ArrayList` type, so I could pass it to methods, which require `ArrayList` (otherwise the code will not compile)? Or I should just ignore this warning? – Ernestas Gruodis Mar 05 '15 at 08:56
  • 1
    @Ernestas Gruodis: This is a limitation that can’t be solved without deep, incompatible changes to the Java language *and* the JVM. But the question is why you are using `Object[]` at all. Java’s type system allows you to keep track of object’s types and if you deliberately decide to drop these type information you shouldn’t complain about lost type information… – Holger Mar 05 '15 at 09:42
  • `Object[]` because method returns various data types. That means the question is answered. – Ernestas Gruodis Mar 05 '15 at 10:59
  • In my case, I have to use `readObject()` method from `ObjectInputStream`, so by default it comes back as an `Object`... so changing it to the right kind of object I need is tricky. – Azurespot May 13 '16 at 00:17
2

This is because if you try to cast Integer to String you will get ClassCastException at runtime. But there will be no ClassCastException here:

    ArrayList<Integer[]> listArr = new ArrayList<>();
    ArrayList<String[]> list = (ArrayList<String[]>) obj[1];
Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275
  • Yes you did, but the runtime cannot know, thats why it is warning you that it cannot enforce the generic type parameter of the cast. – eckes Mar 05 '15 at 08:44
  • I believe `ArrayList` is a concrete type generic. To avoid the aforementioned issue, one must do `ArrayList>` – IgorGanapolsky Jan 19 '16 at 15:28
  • Hey! I have two questions regarding your answer: if no ClassCastException is thrown in your example, what would actually happen? Undefined behaviour? Random values? And secondly: does this mean, that writing my own wrapper class for `ArrayList `(or whatever the Collection is you want to have) is a valid way out of the issue? I mean, if I had something like `MyStringList` which internally would store an `ArrayList`, then it again could throw the classCastException at runtime - is this a legit way out of the problem? – dingalapadum Feb 07 '19 at 14:41
  • Rethinking: is this reasoning correct: the cast will go through without a ClassCastException, but later on when we try to do something with one of those elements (which won't be of the appropriate type, we actually will get a ClassCastExcpetion, right? – dingalapadum Feb 07 '19 at 15:26
2

The compiler complains

ArrayList<String[]> list = (ArrayList<String[]>) obj[1]

because a cast is a runtime check. So at runtime your ArrayList<String[]> could be a ArrayList<Whatever[]>, because the type of obj is unknown.

Ben Win
  • 830
  • 8
  • 21