0

I recently came across code downcasting an object to a String after an instanceof check.

Object obj = this.get(key);
if(obj instanceof String) {
    return (String) object;
}

I'm a bit confused on how this works. My understanding is that it doesn't work with generics because Type Erasure leaves the instance with no reference to the original class. Additionally, my understanding (corroborated by this answer) is that during runtime there is no difference between upcasting to Object and using a generic type T. Does that mean that the only reason instanceof is invalid for generics is due to the compile time checks listed here?

The type of the RelationalExpression operand of the instanceof operator must be a reference type or the null type; otherwise, a compile-time error occurs.

It is a compile-time error if the ReferenceType mentioned after the instanceof operator does not denote a reference type that is reifiable (§4.7).

If a cast of the RelationalExpression to the ReferenceType would be rejected as a compile-time error, then the instanceof relational expression likewise produces a compile-time error. In such a situation, the result of the instanceof expression could never be true.

Despite that, answers like those in this question seem to imply there is a difference in the runtime behavior. Is it that objects typically do contain type information at runtime, but that is erased in the case of generics? How would that type information not be erased in the case of upcasting?

Hopefully someone can identify where my knowledge gap is. I read up a bit on the behavior of instanceof here in an attempt to clarify, but it was a bit beyond me and I was hoping for a more direct answer.

Community
  • 1
  • 1
Kyle
  • 1,978
  • 1
  • 18
  • 30

1 Answers1

2

Objects always knows what type they really are.

Type erasure means that generic types are "lost" in the compiled code. E.g. List<String> is compiled to a raw List, logically equivalent to a List<Object>

However, assigning a String to a variable declared as an Object does not cause type erasure, because:

  1. It's not a generic, so type erasure is not applicable.

  2. The object knows it's a String, regardless of the declared type of the variable.
    That's why you can ask it:

    • Are you a string? x instanceof String

    • What are you? x.getClass()

Andreas
  • 154,647
  • 11
  • 152
  • 247
  • I think I follow. So `boolean fn(T x){ return x instanceof String;}` will work, and `boolean fn(T x){ return x instanceof T;}` will fail as T effectively doesn't exist at runtime (it would always be `Object`)? – Kyle May 06 '19 at 17:47
  • 1
    @Kyle that is correct. That code will fail to compile. – Louis Wasserman May 06 '19 at 17:52