Mariusz Prowaźnik

o programowaniu w Javie, Scali i Clojure.


Java Puzzlers

Ostatnio wpadła w moje ręce bardzo interesująca książka: Java Puzzlers autorstwa Joshua Bloch, Neal Gafter. Opisuje ona ciekawe "kruczki" języka java w postaci 95 czyli krókich i pozornie prostych kawałków kodu. Do każdego z Puzzli, bo tak autorzy je nazywają, dołączony jest komentarz, w którym omawiają, dlaczego program zwraca zaskakujący i zupełnie inny wynik, niżby się wydawało po przeanalizowaniu działania kodu.

Co tu dużo mówić, czas na kilka przykładów:

Puzzle 3: Long Division

public static void main(String[] args) {
        final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
        final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
        System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);
    }
Jaki będzie wynik działania programu? Na pierwszy rzut oka
1000
W rzeczywistości
5
Dzieje sie tak, ponieważ 24 * 60 * 60 * 1000 * 1000 składa się z wyrażeń typu int, więc wynik jest też typu int, a rzutowana do long jest dopiero podczas stworzenia stałej MICROS_PER_DAY. Ponieważ wartość 24 * 60 * 60 * 1000 * 1000 jest zbyt duża by być przechowana jako int, otrzymujemy błędny wynik.

Aby program zadziałał prawidłowo, należy go zmodyfikować w taki sposób:
public static void main(String[] args) {
        final long MICROS_PER_DAY = 24L * 60 * 60 * 1000 * 1000;
        final long MILLIS_PER_DAY = 24L * 60 * 60 * 1000;
        System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);
    }

Puzzle 20: What's My Class?

package com.javapuzzlers;
public class Me {
   public static void main(String[] args) {
      System.out.println(
         Me.class.getName().replaceAll(".", "/") + ".class");
   }

}
Najpierw zostaje pobrana nazwa klasy, czyli com.javapuzzlers.Me, a potem wydawałoby się, że następuje zamiana wszystkich łańcuchów "." na "\" i zostaje dodany łańcuch ".class". Spodziewany wynik: com/javapuzzlers/Me.class
Wynik rzeczywisty: ///////////////////.class

Winny temu jest fakt, że metoda String.replaceAll przyjmuje jako pierwszy parametr wyrażenie regularne, a wyrażenie regularne "." oznacza dowolny znak.
Jak powinien wyglądać kod, aby zwracał oczekiwany rezultat:
package com.javapuzzlers;
public class Me {
   public static void main(String[] args) {
      System.out.println(
         Me.class.getName().replaceAll("\\.", "/") + ".class");
   }
}
Albo jeszcze lepiej:
package com.javapuzzlers;
import java.util.regex.Pattern;
public class Me {
   public static void main(String[] args) {
      System.out.println(Me.class.getName().
          replaceAll(Pattern.quote("."), "/") + ".class");
    }
}

Puzzle 54: Null and Void

public class Null {
    public static void greet() {
        System.out.println("Hello world!");
    }
    public static void main(String[] args) {
        ((Null) null).greet();
    }
}
Gdy analizowałem ten kod, stwierdziłem, że powinien wyrzucić NullPointerException. No bo jak można wywołać metodę dla null? Jednak po wykonaniu, program wyprintuje "Hello world!".
Metoda greet() jest statyczna. Dlatego, podczas rzutowania (Null) null wartość null, jest ignorowana.

Puzzle 67: All Strung Out

public class StrungOut {

    public static void main(String[] args) {
        String s = new String("Hello world");
        System.out.println(s);
    }
}

class String {

    private final java.lang.String s;

    public String(java.lang.String s) {
        this.s = s;
    }
    public java.lang.String toString() {
        return s;
    }
}
Kod, choć dziwny, wydawałoby się, że zadziała i wyrzuci Hello world. Jednak nie zadziała:
Exception in thread "main" java.lang.NoSuchMethodError: main
Dziwne, no przecież jest tam metoda main. Ale jednak nie ma, bo oczekiwana jest metoda main przyjmująca jako parametr tablicę typu java.lang.String, a ta tutaj przyjmuje tablicę obiektów typu stworzonego przez nas.

Brak komentarzy :

Prześlij komentarz