2011-02-07 3 views
13

Ho trovato un bug insolito nel mio codice recentemente attraverso test ad hoc casuali. Quindi, ho fatto un test per questo.Nell'operatore ternario di Java, il primo argomento può essere valutato anche se l'espressione ha dato come risultato un valore falso?

Ecco il mio banco di prova:

SampleRequest request = new SampleRequest(); 
    request.setA(null); 
    request.setB(null); 
    assertEquals(null, request.getAOrB()); 

A e B sono definiti come tipi java.lang.Integer e hanno metodi setter diretti per impostare i loro valori nella richiesta.

Esiste anche un'enumerazione. Ha un valore intero primitivo e un metodo utilizzato in questo codice. Inserirò le parti pertinenti qui:

enum Swapper { 
public int c; 
Swapper findSwapperToUse(final int a) { 
    for(Swapper swapper : values()) { 
     if(swapper.c == a) { 
      return swapper; 
     } 
    } 
    return null; 
} 
} 

Ora, ecco il metodo che è fonte di confusione. Se si richiama il metodo di test su quel metodo, si ottiene un NPE, ma sull'ultima riga del metodo.

public class SampleRequest { 
    private Integer A; 
    private Integer B; 

    public void setA(final Integer A) { 
     this.A = A; 
    } 

    public void setB(final Integer B) { 
     this.B = B; 
    } 


public Integer getAOrB() { 
    return A != null ? Swapper.findSwapperToUse(A).c 
     : B; 
} 
} 

Nel test, sia A che B sono impostati su null. Pertanto, A! = Null restituisce false. Tuttavia, ottengo una NullPointerException al numero di riga per la riga: B.

La mia ipotesi è che per qualche motivo la prima espressione, Swapper.findSwapperToUse (A) .c, sia in fase di valutazione, e pertanto A.intValue() viene richiamato tramite autoboxing, risultante in NullPointerException sul valore null. Attraverso il debug, è noto che findSwapperToUse() non è invocato.

Tuttavia, secondo questa questionthis non dovrebbe accadere: Java ternary (immediate if) evaluation

L'espressione operando non scelta non viene valutato per quel particolare valutazione dell'espressione condizionale.

Restituire un valore Null (B) non darà come risultato NullPointerException - è perfettamente corretto restituire qui un risultato nullo.

Che diavolo sta succedendo?

EDIT: Ho dimenticato di aggiungere che ho modificato il codice per evitare questo utilizzando un dritto fino if - il seguente codice funziona come previsto:

public Integer getAOrB() { 
    if(A != null) { 
     return Swapper.findSwapperToUse(A).c; 
    } 
    return B; 
} 
+2

Il codice che hai dato non sarebbe la compilazione (ad esempio la mancanza di tipo di ritorno). Si prega di fornire un esempio breve ma * completo * e vedremo cosa sta succedendo. –

+0

Come si può essere _ABSOLUTAMENTE__ certi che A sia nullo? –

+0

Ho fornito il tipo di reso, mi dispiace - era Swapper. @Thor - a causa del test (request.setA (null), request.setB (null)) – MetroidFan2002

risposta

22

Credo che il problema è causato dal fatto che compilatore inferisce il tipo di tutta l'espressione

A != null ? Swapper.findSwapperToUse(A).c : B 

come int dal tipo di Swapper.c e, pertanto, tenta di applicare la conversione di unboxing a B.

Ecco il relativo estratto the JLS, §15.25:

  • Altrimenti, se il secondo e terzo operandi hanno tipi convertibili (§5.1.8) per tipi numerici, allora ci sono diversi casi:
    • ...
    • Altrimenti, promozione numerico binario (§5.6.2) viene applicata al operando tipi, e il tipo dell'espressione condizionale è pr tipo omotato del secondo e del terzo operando . Si noti che la promozione numerica binaria esegue la conversione di unboxing (§5.1.8) e la conversione del set di valori (§5.1.13).

È possibile impedire che aggiungendo il seguente getto:

A != null ? (Integer) Swapper.findSwapperToUse(A).c : B 
+1

Ben chiazzato Sir –

+0

Questo è ridicolo, ma è quello che era! Grazie. – MetroidFan2002

+2

:(Dovrebbe esserci un flag del compilatore per disabilitare l'autoboxing per catturare queste cose: –

0

Il tuo metodo findSwapperToUse sta tornando nulla, e si impossibile fare null.c.

Per assicurarsi di questo, vorrei modificare il codice per leggere:

public Integer getAOrB() { 
    if(A != null) { 
     Swapper foundSwapper = Swapper.findSwapperToUse(A); 
     return foundSwapper.c; 
    } 
    return B; 
} 
+0

Questo è quello che ho pensato anch'io, ma non spiegherebbe il bit in fondo alla domanda. –

+0

L'OP ha detto esplicitamente che questo metodo non è invocato. Il che non è una sorpresa considerando che A è nullo, quindi non può essere un argomento per una funzione che accetta 'int'. –

+0

@Sergey oops hanno perso quel pezzettino :) – Rich