1

I wonder why the following cast is unchecked:

== Update 1 ==

I know that we have type erasure at runtime. Mind the input parameter is a ArrayListand not some random list.ArrayList implements List and RandomAccess. I know this cast will not work with LinkedList or MySpecialArrayList. but the parameter of this method forbids that. I know (until people remove Listor RandomAccessfrom ArrayListthe cast will not fail at runtime, but why is the cast unchecked?

== End update 1 ==

  private static <L extends List<GenericTokenType<?>> & RandomAccess> L castArrayList(ArrayList<GenericTokenType<?>> instance) {
    return (L) instance;
}

I simplified this to [still warning]

  private static <L extends List> L castArrayList(ArrayList instance) {
    return (L) instance;
}

and [no warning]

 private static List castArrayList(ArrayList instance) {
    return (List) instance;
}

Why does this not work. L is a List (not the runtime type, but the compiler should get that.

To rephrase the question: Why doesn't it work with a generic parameter return type? Thanks

Florian Reisinger
  • 2,638
  • 4
  • 23
  • 34

2 Answers2

5

In your first and second example, you're casting an ArrayList to a type called L. According to the generic constraints, L must also implement the List interface. Therefore, L might be ArrayList, LinkedList, Stack or Vector, or some other class that you create that implements List.

So far so good, right?

Now let's suppose L is an ArrayList the expression is equivalent to:

(ArrayList) instance

Obviously that will work.

But what if L is LinkedList?

(LinkedList) instance

That doesn't work because you can't force an array list to be a linked list! They are different data structures!

Why does the third work then?

In the third example, you're stating explicitly that "I want this ArrayList to be List". That works because the type you're casting instance to is certain. ArrayList implements List, so it must be compatible with it!

The Method will not compile as LinkedList is a subtype of List and this does not work without a cast.... Please explain, I only allow ArrayList in and no other subclass of List

Well, yes you only allow ArrayList to be passed in to the method. But the return type of the method is L. From the generic constraints, we know that L can be any type that implements List. However, you can't convert ArrayList to any type that implements List by casting.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • You can add example in the following call scenario: ```LinkedList> list = castArrayList(arrayList)``` – maress Dec 01 '16 at 08:17
  • I do not know, why this gets upvoted. The Method will not compile as `LinkedList` is a subtype of `List` and this does not work without a cast.... Please explain, I only allow `ArrayList`in and no other subclass of `List`.... – Florian Reisinger Dec 01 '16 at 08:30
  • @FlorianReisinger yes, you only allow ArrayList in, but the return type of the method is L, which can be anything that implements List, according to the generic constraint. You seem to want to write a method that converts an array list to any list by casting, which is not possible. – Sweeper Dec 01 '16 at 08:56
  • @Sweeper: could you please edit your answer so that I can remove my downvote. Didn't think that's possible. – Florian Reisinger Dec 01 '16 at 13:27
  • @FlorianReisinger Edited. – Sweeper Dec 01 '16 at 13:30
4

L will be erased to List, since that is the lower bound. However, the uncheckedness comes from the fact that L can technically be a subclass of List, but due to type erasure, only a cast to (at most) List can be performed. The actual cast will be performed later when the concrete type is available.

You can see this nicely in bytecode too:

  public static void main(java.lang.String[]) throws java.lang.Exception;
    Code:
       0: new           #2  // class java/util/ArrayList
       3: dup
       4: invokespecial #3  // Method java/util/ArrayList."<init>":()V
       7: invokestatic  #4  // Method castArrayList:(Ljava/util/ArrayList;)Ljava/util/List;
      // Cast is done here, after returning
      10: checkcast     #2  // class java/util/ArrayList
      13: astore_1
      14: return

  public static <L extends java.util.List> L castArrayList(java.util.ArrayList);
    Code:
       0: aload_0
       // No 'checkcast' here
       1: areturn
Jorn Vernee
  • 31,735
  • 4
  • 76
  • 93
  • Could you please explain what "unsafe" is in Java then? Because we agree this cast will not fail unless the `ArrayList`class is no longer implementing `List`. I know that `L` can be more than list, but the cast will still work. – Florian Reisinger Dec 01 '16 at 08:39
  • 1
    @FlorianReisinger The cast will not always work. If you make it so `L` infers to another subtype of `List`, you'll get a class cast exception, and it will be in the calling code, not in the `castArrayList` method. This could be confusing, so the warning is there to point out the unsafe situation. The cast is "unchecked", because it is blindly excepted by the `castArrayList` code, it will be checked that it is valid after returning. – Jorn Vernee Dec 01 '16 at 08:54
  • @FlorianReisinger Here is also an example of when this cast fails: [Ideone link](http://ideone.com/fsJJ9o) – Jorn Vernee Dec 01 '16 at 09:01
  • Thanks for the link, I did not expect that to work. I expected the compiler to see that I return a `List`, which is clearly not a `LinkedList` – Florian Reisinger Dec 01 '16 at 13:26
  • 1
    @JornVernee or better said, generics are poly expressions, they depend on the context, and the context of a generic is only known once there are callers; thus `checkcast` is moved into the callers; good answer – Eugene Sep 26 '18 at 09:44