tifyty

pure Java, what else ?

A feltételes operátor c ? a : b

Mostanában ért egy kis meglepetés, mert a lefordított Java nem úgy működött, ahogyan vártam. Ez persze gyakran megesik ebben a szakmában: ilyenkor kezdődik a debuggolás. Most is ez volt, de ahelyett, hogy a saját kódomban találtam volna hibát, valahol máshol találtam. Nem jó kódot generált a javac fordító. (Gondoltam én.)

Az osztály, amelyik demonstrálja a gondot a következő:

public class Bug{
    public static Number q(Number in) {
        return in instanceof Long ? 1L : 1.0;
    }
}

Egyszerűen csak annyit kellene tennie, hogy egy Long vagy egy Double típusú 1 értéket ad vissza, attól függően, hogy az argumentum Long vagy Double típusú. A kód azonban minden esetben Double típust ad vissza, és ez nem jó.

Megnéztem, hogy milyen kódot generál a javac. Javap disassemblerrel visszafordítva láthatjuk, hogy (ugyan elég bután, hiszen a javac nem optimalizál) valóban ez történik:

public static java.lang.Number q(java.lang.Number);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0       
         1: instanceof    #2                  // class java/lang/Long
         4: ifeq          11
         7: dconst_1      
         8: goto          12
        11: dconst_1      
        12: invokestatic  #3                  // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
        15: areturn       

Akár Long instance az argumentum változó, akár nem, mindenképpen double típusú konstans 1 értéket tölt az operatív stack-re a bytecode (dconst_1). És ez akkor is így van, ha bármilyen kifejezést írok a kérdőjel elé, és akkor is ha megfordítom a kettőspont előtt és után levő értékeket. Ez a kód így bizony nem jó. Mégsem a javac-ban van a hiba, mert a generált kód ha nem is jó (nekem) mégis pontosan az, aminek lennie kell a Java szabvány definíciója szerint.

(Akkor hol a hiba? Hát a fejemben!)

A szabvány, ami elérhető a http://docs.oracle.com/javase/specs/jls/se7/html/index.html oldalról azt mondja a ternary operátorról, hogy

Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:

Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.

Ami annyit tesz, hogy ha a második és harmadik operandus numerikus értékké konvertálható, akkor sok eset lehet, és ezek között a mi esetünkre vonatkozó szerint “numeric promotion” fog történni az operandusokkal, a §5.6.2-nak megfelelően és az egész feltételes kifejezés típusa is ennek megfelelő lesz.

Az §5.6.2 paragrafus pedig azt mondja, hogy

If either operand is of type double, the other is converted to double.

Vagyis, ha az egyik operandus double akkor a másik is double típusúvá lesz konvertálva.

2 responses to “A feltételes operátor c ? a : b

  1. Zsolt János július 21, 2012 10:23 du.

    Hú, ez érdekes. Köszi!

  2. István Benedek augusztus 4, 2012 9:38 du.

    Tetszik a probléma mert rámutat arra, hogy valszeg a három operandusú operátor definíciós szinten nem equivalens más java programozási elemekkel. Várom mikor jön az a cikk, hogy mennyi baromságot megenged a java, amit nem kellene… illetve, hogy miket kellene kidobni belőle..:)

Vélemény, hozzászólás?

Adatok megadása vagy bejelentkezés valamelyik ikonnal:

WordPress.com Logo

Hozzászólhat a WordPress.com felhasználói fiók használatával. Kilépés / Módosítás )

Twitter kép

Hozzászólhat a Twitter felhasználói fiók használatával. Kilépés / Módosítás )

Facebook kép

Hozzászólhat a Facebook felhasználói fiók használatával. Kilépés / Módosítás )

Google+ kép

Hozzászólhat a Google+ felhasználói fiók használatával. Kilépés / Módosítás )

Kapcsolódás: %s

%d blogger ezt kedveli: