среда, 14 мая 2008 г.

Java Puzzle 90: It's Absurd, It's Pain, It's Superclass

Следующая программа очень простая и ничего не делает. Но к ней есть два вопроса: почему она не компилируется и как это исправить?
public class Outer {
class Inner1 extends Outer {}
class Inner2 extends Inner1 {}
}

Проблема в том, что при создании класса вызывается "конструктор по-умолчанию", который создаётся автоматически и содержит вызов конструктора суперкласса, а этот вызов выглядит так: this.super(). Теперь становится понятно, почему компилятор ругается "cannot reference this before supertype constructor has been called". Это запрещено согласно спецификации (JLS 8.8.7.1). А исправить можно добавив конструктор без параметров в класс Inner2:
public class Outer {
class Inner1 extends Outer {}
class Inner2 extends Inner1 {
Inner2() {
Outer.this.super();
}
}
}

Такая конструкция уже компилируется, хотя и выглядит очень странно. Кстати, парсер IntelliJ IDEA продолжает показывать ошибку в этом месте.
Авторы предлагают решение лучше и советуют: Когда вы пишете вложенный класс спросите себя, действительно ли нужен доступ к членам класса, в который вы его вкладываете? Если нет, то делайте вложенный класс статическим. Я вообще не люблю вложенные классы. Они усложняют работу с наследованием, а так же с generics и reflection.

Java Puzzle 89: Generic Drugs

В предыдущей задаче мы поняли, что не надо использовать raw type. Написали программу, расставив везде нужные декларации. Что напечатает следующая программа?
public class LinkedList {
private Node head;
private class Node {
E value;
Node next;
Node(E value) {
this.value = value;
this.next = head;
head = this;
}
}
public void add(E e) {
new Node(e);
}
public void dump() {
for (Node n = head; n != null; n = n.next)
System.out.print(n.value + " ");
}
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add("world");
list.add("Hello");
list.dump();
}
}

А она не хочет компилиться! Дело в том, что типы E разные для класса LinkedList и класса Node. Избегайте затенения параметра типа. Авторы говорили об опасности затенения в головоломках 71, 73 и 79. После этого сразу видно, что надо добавить второй параметр конструктору класса Node и слегка изменить метод add:
public class LinkedList {
private Node head;
private class Node {
F value;
Node next;
Node(F value, Node next) {
this.value = value;
this.next = next;
}
}
public void add(E e) {
this.head = new Node(e, this.head);
}
public void dump() {
for (Node n = head; n != null; n = n.next)
System.out.print(n.value + " ");
}
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add("world");
list.add("Hello");
list.dump();
}
}

Блин, с этими generics, как по минному полю ходишь. Хотя с template классами в C++ тоже не так всё просто...

вторник, 13 мая 2008 г.

Vintersorg


Заглянул сегодня в Кайлас, а там дисков прибыло. Не всё, конечно, что я хотел купить, но хоть кое-что. Сегодня это был альбом Solens Rötter (2007), что в переводе со шведского означает "Корни Солнца".
Во-первых, меня очень порадовало возвращение к шведскому языку. На мой взгляд это язык №1 для тяжёлой музыки. Сразу вспоминаются их ранние работы Till Fjälls и Ödemarkens Son.
Во-вторых, они закончили с непонятными экспериментами в последних работах и альбом лишь слегка не дотягивает до ранних работ, но музыка, всё-равно, совершенно волшебная! Очень рекомендую!!!

воскресенье, 11 мая 2008 г.

День Победы

Мишка ходил на праздник к памятнику. Впечатлился. Марширует, как солдат. Честь отдаёт. Постоянно поздравляет всех с Днём Победы. Где бы ему такую одёжку найти:

Поубивал бы гадов

Хорошо, что я был с бабушкой. Какая-то сука позвонила: мама, мама, я человека насмерть сбил, денег надо, бла-бла-бла...
Бабушку аж трясёт всю. Я-то сразу заподозрил неладное, когда она мне трубку стала передавать, на том конце отсоединились. Блин! Её до вечера колбасило.
Я, конечно, быстро папу набрал, проверил, что всё в порядке. Бабушку как мог успокоил. Позвонил в полицию, а там говорят, что такие случаи последнее время участились. За день до этого, мы сидели на кухне и новости по радио слушали. Там тоже об этом говорили. Спрашивали в полиции, не знаю ли я телефона, с которого мне звонили. Но на домашнем нет автоответчика, а сами они проверить, кто мне звонил, видите ли, не могут.
Хорошо, что я был с бабушкой. Теперь то она готова к таким звонкам, но а если бы она была одна? Сабж!

Крутые парни

Жаль, что обычно не таскаю с собой фотоаппарат.
Приезжаю сегодня на Балтийский вокзал. 7 утра. Солнце слепит. Машин практически нет. Лепота...
А по Обводному проезжают два мотоциклиста полной защите. Все в чёрном. И только одна деталь портит всю картину. У каждого мотоцикла нелепая жёлтая крыша с надписью ОХРАНА. Повеселило ;)

пятница, 9 мая 2008 г.

С Днём Победы

Поеду в Таллинн на праздник. Если бабушка будет в состоянии, то дойдём до памятника. Вы же можете возложить цветы виртуально.


Джентльмены! Поздравляю вас с победой!

понедельник, 5 мая 2008 г.

Java Puzzle 88: Raw Deal

Что напечатает следующая программа?
import java.util.Arrays;
import java.util.List;
public class Pair<T> {
private final T first;
private final T second;
public Pair(T first, T second) {
this.first = first;
this.second = second;
}
public List<String> toArray() {
return Arrays.asList(
String.valueOf(this.first),
String.valueOf(this.second));
}
public static void main(String[] args) {
Pair pair = new Pair<Object>(23, "skidoo");
for (String str : pair.toArray()) {
System.out.println(str);
}
}
}

Удивлены? Я тоже. Она просто не скомпилируется. Несовместимость типов в строке:
    for (String str : pair.toArray()) {

Что происходит? Ведь метод toArray всегда возвращает список строк! Проблема в том, что локальная переменная pair использует raw type. А это означает, что все поля и методы тоже будут использовать raw types. Т.е. метод toArray в таком случае возвращает просто список List. Бороться с этим просто. Измените декларацию переменно таким образом:
    Pair<Object> pair = new Pair<Object>(23, "skidoo");

или ещё более просто:
    Pair<?> pair = new Pair<Object>(23, "skidoo");

И запомните, что тип List (raw type) отличается от List<Object> (parametrized type).

А для меня, как разработчика пакета JavaBeans, который в полный рост использует reflection, это знание ОЧЕНЬ важно. Я вышел на новый уровень понимания generics!

Java Puzzle 87: Strained Relations

Тут задачка на равенства. Отношение равенства должно быть рефлексивным (reflexive), транзитивным (transitive) и симметричным (symmetric). Это в теориии...
А в Java определяет ли оператор сравнения == отношение равенства для значений примитивных типов? Если нет, то какое из трёх свойств оно нарушает?

В головоломке 29 мы узнали, что оператор сравнения не рефлексивный, так как Double.NaN == Double.NaN возравщает false.

Головоломка 35 поможет понять, что оператор сравнения не транзитивный. При сравнении двух числовых значений сначала осуществляется приведение типов. Но аккуратнее, конвертация int или long в double может привести к потере точности. А это чревато!
public class Transitive {
public static void main(String[] args) {
long x = Long.MAX_VALUE;
double y = (double)Long.MAX_VALUE;
long z = Long.MAX_VALUE - 1;
System.out.println(x == y);
System.out.println(y == z); // surprise!
System.out.println(x == z);
}
}

Haluz

Хороший on-line квест, продолжающий традиции Samorost 1 и 2. И тоже двухсерийный...

воскресенье, 4 мая 2008 г.

Опять в Таллинн

На выходные собираюсь поехать в Таллинн, встретить с бабушкой праздник. Осознал, что ОЧЕНЬ хочу, чтобы из нашего окна звучала патриотическая музыка. Надо качать!
Тут, тут и тут...

Opeth


3 июня на лейбле Roadrunner Records выходит новый диск группы Opeth: Watershed (2008). Очень люблю их творчество, особенно альбом Morningrise (1996), который слушаю прямо сейчас.
Я чего бучу поднял раньше времени. Они сняли клип на песню Porcelain Heart. Съёмки проходили в замке Bogesund (Vaxholm, Швеция), построенном в 1640 г. И клип красивый, и песня клёвая!
А вот так развлекаются настоящие фанаты:

среда, 30 апреля 2008 г.

Мороженое

Катался в Таллинн на днях. Видел там мороженое фирмы Premia. Фишка в том, что у них появилось мороженое в стиле ретро. В старые добрые советские времена оно по 20 копеек, кажется, было. Я даже не удержался - купил попробовать. Вкусно! И ностальгия...

пятница, 25 апреля 2008 г.

Java Puzzle 86: Poison-Paren Litter

Можете ли вы придумать корректное выражение, которое превращается в некорректное, если входящее в него выражение взять в скобки?

Обычно добавление пары скобок используется для улучшения читаемости кода. Однако, существует два случая, когда это не так (вспомните задачки 33 и 64).

int i = -2147483648; - корректно
int i = -(2147483648); - некорректно

long l = -9223372036854775808L; - корректно
long l = -(9223372036854775808L); - некорректно

Java Puzzle 85: Lazy Initialization

Следующему классу лень инициализировать себя обычным путём, поэтому он делает это на фоновом потоке. Что программа напечатает? Гарантируется одинаковый результат для каждого запуска?
public class Lazy {
private static boolean initialized = false;
static {
Thread thread = new Thread(
new Runnable {
public void run() {
initialized = true;
}
}
);
thread.start();
try {
thread.join();
} catch (InterruptedException ie) {
throw new AssertionError(ie);
}
}
public static void main(String[] args) {
System.out.println(initialized);
}
}

Кажется, что программа должна каждый раз печатать true, но нет! Она просто виснет.
Симпатичный deadlock. Попробую объяснить.
Понятно, что при обращении к методу main JVM начинает инициализировать класс. И только когда инициализация закончится будет вызван метод main. Во время инициализации запускается метод run. Однако, этот метод перед установкой initialized в true делает тоже проверку на то, что класс уже проинициализирован. Таким образом получается, что основной поток ждёт завершения фонового потока для завершения инициализации, а фоновый поток ждёт завершения инициализации. Вуаля!

Guitar Heroes


Скачал в интернете Guitar Heroes: Album (2007). Чисто инструментальный альбом, записанный известными финскими гитаристами:
    Timo Tolkki from Stratovarius
    Alexi Laiho from Children Of Bodom
    Roope Latvala from Children Of Bodom
    Emppu Vuorinen from Nightwish
    Esa Holopainen from Amorphis
    Nino Laurenne from Thunderstone
    Mr Crab from Bleak
    Mikko Salovaara from Kiuas
    Tuomo Louhio from Impaled Nazarene
    Sakari Hietala from Tarot
    Euge Valovirta from Godsplague
Некоторые песни очень даже цепляют. Например, Hellbilly. Впечатления, в основном, положительные. Теперь думаю, где бы диск купить. Кайлас совсем испортился...

Без очереди

Пришёл в банк погасить задолженность по кредитной карте. Там, как обычно, большая очередь. Напрягает стоять. Даже думал получить код для управления своим счётом через Интернет, хотя мне эта идея не очень нравится. Но один из сотрудников банка сказал, что при наличии паспорта и кредитной карты счёт можно погасить без очереди. Это радует...

вторник, 22 апреля 2008 г.

The Sniper

На flash делают уже вполне достойные игры. Например, Снайпер. Интересная игра про высадку союзных войск в Нормандии.
И вот дают нам очередную миссию:

Вот в прицел мы видим фрицев:

Смотреть можно не только в прицел:

Крестиками помечаются трупы, так что искать придётся всё равно через прицел.

Между миссиями пишут разные сообщения, сопровождая их прикольными картинками:

А вот и мой результат.

Java Puzzle 84: Rudely Interrupted

В этой задаче мы пытаемся прервать исполнение текущего потока, а затем проверяем удалось это или нет. Что напечатает следующая программа:
public class SelfInterruption {
public static void main(String[] args) {
Thread.currentThread().interrupt();
if (Thread.interrupted()) {
System.out.println("Interrupted: "
+ Thread.interrupted());
} else {
System.out.println("Not interrupted: "
+ Thread.interrupted());
}
}
}

Вы удивитесь, но она напечатает "Interrupted: false"
Но почему? Дело в том, что вызов метода Thread.interrupted() всегда очищает состояние флага interrupted текущего потока. Ну мы и получили, что первый вызов вернул true, а второй - false. Для решения проблемы нужно вызывать метод Thread.currentThread().isInterrupted(), который не изменяет состояние флага interrupted.

Java Puzzle 83: Dyslexic Monotheism

Однажды один мужик режил что он единственный такой исключительный пёс. И тогда он написал класс, думая что написал singleton:
public class Dog extends Exception {
public static final Dog INSTANCE = new Dog();
private Dog() {
}
public String toString() {
return "Woof";
}
}

Но он ошибался. Во всём. Сможете ли вы создать второй экземпляр класса Dog не используя reflection?

Ну тут надо сообразить, что класс Exception является Serializable. А дальше всё просто. Эта проблема и её решение были описаны в книге Effective Java: singleton, который имплементирует интерфейс Serializable должен определять readResolve метод, который возвращает единственный экземпляр:
public class Dog extends Exception {
public static final Dog INSTANCE = new Dog();
private Dog() {
}
private Object readResolve() {
return INSTANCE;
}
public String toString() {
return "Woof";
}
}

А иначе при десериализации вы получите другой экземпляр.