Baza Wiedzy

Wyrażenie warunkowe (conditional operator)

W dzisiejszym wydaniu serii JavaTraps zajmiemy się conditional operatorem (inaczej ternary operator, operator trójargumentowy, lub wyrażenie warunkowe). Przypomnijmy jego składnię:

(condition or expression)? operand1 : operand2;

// co jest równoważne z:
if (condition)
    operand1;
else
    operand2;

Tyle wstępem przypomnienia, a teraz przejdźmy do dzisiejszej zagadki. Pochodzi ona z serii Java Puzzlers, która zainspirowała mnie wczoraj. Pytanie brzmi oczywiście, co wyświetli się na ekranie?

import java.util.Random;

public class JavaTraps002 {
	public static void main(String args[]) {
		Random rand = new Random();
		boolean check = rand.nextBoolean();

		Number number = (check || !check)?
				new Integer(1) : new Double(2.0);

		System.out.println("Wynik: "+number);
	}
}

Kurs Programowania Java

Jak widać przykład nie jest strasznie skomplikowany, dostępne odpowiedzi to:

A) Wynik: 1

B) Wynik: 2.0

C) Błąd kompilacji

D) Inna odpowiedź

  

 

 

 

 

 

 

Rozwiązanie

Przypomnijmy sobie jak wyglądał kod:

import java.util.Random;

public class JavaTraps002 {
	public static void main(String args[]) {
		Random rand = new Random();
		boolean check = rand.nextBoolean();

		Number number = (check || !check)?
				new Integer(1) : new Double(2.0);

		System.out.println("Wynik: "+number);
	}
}

A poprawna odpowiedź to:

D) Inna odpowiedź. Wyświetlone zostanie Wynik: 1.0

Zastanawiacie się jak to możliwe? Już wyjaśniam.

W pierwszej kolejności tworzymy sobie obiekt random do wygenerowania jednej wartości typu boolean. Tutaj oczywiście nie ma żadnej pułapki. Następnie chcemy utworzyć obiekt typu Number i przypisać do niego, albo wartość Integer(1), lub Double(2.0). Ponieważ wyrażenie (check || !check) zawsze zwraca true. Spodziewamy się, więc, że do referencji number zostanie przypisany obiekt new Integer(1). No i w zasadzie tak się dzieje. Jest tylko jedno małe ale... Wyświetlając wartość number widzimy na ekranie 1.0. Jakim cudem?

W specyfikacji języka odnajdujemy odpowiedź dlaczego tak się dzieje. Konkretnie chodzi o zapis:

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. Note that binary numeric promotion performs unboxing conversion (§5.1.8) and value set conversion (§5.1.13).

W naszym przypadku, kompilator przewiduje, że wynikiem zawsze będzie typ numeryczny: Integer, albo Double. Następuje więc unboxing do typów prostych int i double, a następnie promocja do najogólniejszego typu, w naszym przypadku int -> double. W następnej kolejności następuje boxing i w wyniku mamy dwie wartości typu Double.

Podobnego zachowania możemy się spodziewać w podobnych przypadkach np Integer, oraz Float, lub innych wymienionych w specyfikacji.

Jak się bronić przed tym zachowaniem?

Najlepiej nie używać conditional operatora w miejscach, gdzie nie jesteśmy pewni jak się zachowa. Bezpieczniej będzie użyć w takim przypadku zwykłego rozgałęzionego warunku if.

Zapisz się do newslettera

Otrzymuj nasz Newsletter z przykładowymi pytaniami rekrutacyjnymi, wyzwaniami programistycznymi i nowościami ze świata Javy, a także informacje o nowych kursach i promocjach.

Traktujemy Twoją prywatność poważnie. Nikomu nie udostępniamy Twojego maila no i zawsze możesz się wypisać.

Komentarze do artykułu

Wyłączyliśmy możliwość dodawania komentarzy. Poniżej znajdziesz archiwalne wpisy z czasów gdy strona była jeszcze hobbystycznym blogiem. Zapraszamy natomiast do zadawnia pytań i dyskusji na naszej grupe na facebooku.

Szkolenie Java WrocławJavaStart na Youtube