How does the ternary operator evaluate the resulting data type?

Given this piece of code

public class Main {

    public static void main(String[] args) {
        foo(1);
        foo("1");
        foo(true?1:"1");
        foo(false?1:"1");
    }

    static void foo(int i){System.out.println("int");}
    static void foo(String s){System.out.println("String");}
    static void foo(Object o){System.out.println("Object");}
}

      

This is the result I am getting:

int
String
Object
Object

I cannot understand why in the last two cases it is called foo(Object o)

instead of foo(int i)

and foo(String s)

. Isn't the return type for a ternary expression evaluated at runtime?

Edit:

What confused me was the statement that

System.out.println((y>5) ? 21 : "Zebra");

      

compiles because (OCA Tutorial - Sybex):

System.out.println () doesn't care about the operators being completely different types, because they can convert both to String

while the point is that println is overloaded to accept object types as input. Completely wrong, imho.

+3


source to share


3 answers


Not a return type for a ternary expression evaluated at runtime?

No, absolutely not. This would be very contrary to how Java works, where is the overload resolution, etc. Always executed at compile time. What would you expect if you did not pass the result to the method, but tried to assign it to a variable? What variable would you specify?



The expression type is determined by the rules of JLS 15.25 . In both cases, the third operand is of type String

, which results in a reference conditional expression, so Table 15.25-E with the result is applied lub(Integer,Object)

. The part lub

references JLS 4.10.4 , which is quite confusing - the type here is not exactly the same as the only one Object

, but in most cases it can be considered that way.

+7


source


Both triplet alternatives must be of the same type. The only common type for Integer and String is Object, so both operands are passed to an object, and the trinity type is defined as an object at compile time.

The compiler then statically binds to the method with the Object parameter.



It doesn't matter that the result is logically ternary determined at compile time — the compiler doesn't work that way. It deals with expressions and types. It must first resolve the type of the ternary expression, not its value, and to do this, it must first find the common type for the operands.

+10


source


This, I suppose, is in addition to the other answers. (Or more specifically for those who want to understand the pedantic answer.)

The result of the ternary conditional for reference types ends up lub(trueType, falseType)

, so in this particular case it is lub(Integer, String)

. lub

is
:

The smallest upper bound or "lub" of the set of reference types is a generic supertype that is more specific than any other generic supertype [...].

To understand lub

, we could do a kind of "scratch" version of the algorithm for simple types as follows.

First, create a table for each type. In one column, write down the superclass hierarchy, and in the other column, write down all implemented interfaces:

+------------------------+------------------------+
|      Integer           |       String           |
+------------------------+---------+--------------+
| classes |  interfaces  | classes |  interfaces  |
+------------------------+---------+--------------+
| Object  | Serializable | Object  | Serializable |
| Number  | Comparable   | String  | Comparable   |
| Integer |              |         | CharSequence |
+---------+--------------+---------+--------------+

      

(The actual algorithm would combine classes and interfaces together as "supertypes".)

Now:

  • remove all superclasses from the bottom up until you come across a regular superclass.
  • remove any interfaces that are not in use.
+------------------------+------------------------+
|      Integer           |       String           |
+------------------------+---------+--------------+
| classes |  interfaces  | classes |  interfaces  |
+------------------------+---------+--------------+
| Object  | Serializable | Object  | Serializable |
| Number  | Comparable   | String  | Comparable   |
| Integer |              |         | CharSequence |
+---------+--------------+---------+--------------+

      

lub

- now the type of intersection given by this common superclass and common interfaces:

lub(Integer, String) =
    Object & Serializable & Comparable

      

Except it's lub

a little tricky here because of Comparable

. Technically, lub(Integer, String)

it gives an infinite type, because we need to lub(Integer, String)

make the (infinite) type argument again Comparable

:

lub(Integer, String) =
    Object & Serializable & Comparable<? extends lub(Integer, String)>

      

The conditional type (boolean ? Integer : String)

is the type that results from the capture conversion to lub(Integer, String)

(yikes!).

The overload was foo(Object)

chosen because it is most applicable to this type.

A few other interesting things about this:

  • If we added an overload foo(Serializable)

    , it will be selected because it is Serializable

    more specific than Object

    .
  • If we added two overloads foo(Serializable)

    and foo(Comparable<?>)

    , we get an ambiguity error.
0


source







All Articles