11

I was playing with ternary operator and noticed something odd. I have code below:

class Main {

  static void foo(int a){
    System.out.println("int");
  }

  static void foo(String a){
    System.out.println("String");
  }

  static void foo(Object a){
    System.out.println("object");
  }

  public static void main(String[] args) {
    foo(2==3 ? 0xF00:"bar");
    System.out.println((2==3 ? 0xF00:"bar").getClass().getName());
  }
}

Which results in

object

java.lang.String

First line of result shows that this instruction passed to foo method with object parameter.

Second line that the instruction itself results in String.

Question:

  1. Why if result is String compiler decides to go with Object?

  2. Is this because of the type ambiguity?

  3. If yes then why getting class name returned java.lang.String?

Community
  • 1
  • 1
Lemonov
  • 476
  • 4
  • 17
  • 1
    You're confusing runtime and compile time types. The type of `(Object)"foo"` is `Object` at compile time and `String` at runtime. – shmosel Jun 13 '18 at 14:08
  • `(2==3 ? 0xF00:"bar")` results in String ("bar") which you do then `getClass()` on – ACV Jun 13 '18 at 14:09
  • Add `println(a)` to `foo(Object)` and it will print the `String` “bar”. – AJNeufeld Jun 13 '18 at 14:21

3 Answers3

9

In Java, you have compile time type information and you have run time type information. Compile time type information is what the compiler can deduce about the type of a value or expression just by looking at it, but without executing it. When the compiler sees the expression

2 == 3 ? 0xF00 : "bar"

It does not know whether 2 == 3 will be true or false, because it does not execute code. So all it knows is that the result can be an Integer, or a String. So when the time comes to pick which foo method to call, it picks the one that accepts Object, since that is the only one that it knows will work in both scenarios.

However, when the code is actually running, 2 == 3 will be false, and the result will be a String, whose getClass() method will return String.class. And that is what you need to note: getClass() does not return the type that the variable had at compile time, but it returns the actual type of the object that the variable holds at run time. i.e.

Object o = "Hello!";
System.out.println(o.getClass());

will print java.lang.String, because even though to the compiler it is an Object, at run time it is actually a String.

Leo Aso
  • 11,898
  • 3
  • 25
  • 46
4

You can skip to Summary if not interested in reading.

Refer the JavaDoc here: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.25

Under 15.25.3. Reference Conditional Expressions It says:

The type of the conditional expression is the result of applying capture conversion

For capture conversion : https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.10

Summary:

For type determination, capture conversion is used, wherein for your example int is first boxed to Integer and then the closest common super class of Integer and String is fetched, which is Object class. So the type for the Conditional Expression is Object and so the method with Object as parameter is called.

Now for second part, the Conditional operator is evaluated first, then it is unboxed and then .getClass() is evaluated. So it prints java.lang.String.

This is also documented here: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.25

Under 15.25. Conditional Operator ? :

At run time, the first operand expression of the conditional expression is evaluated first. If necessary, unboxing conversion is performed on the result.

Shubham Kadlag
  • 2,248
  • 1
  • 13
  • 32
3

At compile stage, the compiler noticed that the result of 2 == 3 ? 0xF00 : "bar" could be int or String. To be compatible with both, it decide to call foo(Object a).

At runtime, the result of 2 == 3 ? 0xF00 : "bar" is String bar.

xingbin
  • 27,410
  • 9
  • 53
  • 103