Julian Rubin
Julian Rubin

Reputation: 1235

Unchecked cast behavior with and without assignment to variable

Why first line in main does not throw ClassCastException while the second does?

import java.util.function.Function;

class Scratch {

    static <T> T getSomething(Function<Integer, T> fun) {
        return (T) fun;
    }

    public static void main(String[] args) {
        Scratch.<String>getSomething(x -> "hello");
        String something = Scratch.<String>getSomething(x -> "hello");
    }
}

Upvotes: 3

Views: 86

Answers (2)

Andy Turner
Andy Turner

Reputation: 140318

The difference is because you don't use the result of the method in the first case, but you do in the second.

A cast is an expression, but it's not a StatementExpression. This means that you can't write this:

(String) somethingReturningAString();

but you can write:

String aString = (String) somethingReturningAString();

At compile time, the compiler inserts checkcast instructions where it needs to, and where it can:

  • It can't insert a cast for the first case, so no checking takes place.
  • It can (and has to) insert a cast in the second case, in order to ensure that it's assigning something that's actually a String to a String variable. As such, it checks the cast, and that fails.

It's worth noting that there are some perhaps unexpected cases where a cast isn't strictly necessary, but is inserted. For example:

Scratch.<String>getSomething(x -> "hello").toString();

would fail with a ClassCastException, since it would be transformed to:

((String) Scratch.getSomething(x -> "hello")).toString();

even though Object has a toString() method, and so it could invoke that without a cast.

Upvotes: 2

Lino
Lino

Reputation: 19926

Generics are only a compile time check (read about type erasure). So at runtime your getSomething() method looks similar to this:

static Object getSomething(Function fun) {
    return fun;
}

Now you see clearly that the first line will never throw an exception

Scratch.getSomething(x -> "hello");

because Function is an Object and thus can be returned without a problem.

The second line however will throw one, because it will look similar to this:

String something = (String) Scratch.getSomething(x -> "hello");

A Function is still an Object so it can be returned from the method, but it is not a String and thus, you get your ClassCastException.

The code compiles fine, because you instruct the compiler that you know what you're doing. You will get an Unchecked cast warning though on this line:

 return (T) fun;

This warning should be an indicator from the compiler to you, the programmer, that it (the compiler) can't be sure that that cast will succeed.

Upvotes: 1

Related Questions