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

Java Puzzle 80: Further Reflection

Что напечатает следующая программа, которая создаёт новый экземпляр объекта используя reflection?
public class Outer {
public static void main(String[] args) throws Exception {
new Outer().greetWorld();
}
private void greetWorld() throws Exception {
System.out.println(Inner.class.newInstance());
}
public class Inner {
public String toString() {
return "Hello world";
}
}
}

Программа напечатает Exception stack trace. А всё потому, что для вложенных (не статических!) классов не действует правило создания конструктора без параметров. В этом случае автоматически добавляется параметр, имеющий тип внешнего класса. Перепишем метод правильным образом:
  private void greetWorld() throws Exception {
Constructor c = Inner.class.getConstructor(Outer.class);
System.out.println(c.newInstance(this));
}

Обратите внимание, что в этом случае можно создать вложенный класс, для которого не установлен экземпляр внешнего класса. Надо только передать null вместо this. Автор рекомендует вообще не использовать reflection для создания экземпляров вложенных классов. Я бы сказал по другому: не используйте то, в чём не разбираетесь.
Тут автор ссылается на раздел 18 своей предыдущей книги Эффективное программирование: старайтесь использовать статические члены класса, когда это возможно.

Java Puzzle 79: It's a Dog's Life

Следующий класс моделирует жизнь домашнего животного. В самом задании забавная игра слов: обычно собаки бегают на заднем дворе (run in the backyard), а наша будет выполняться в фоновом режиме (run in the background). Что программа напечатает?
public class Pet {
public final String name;
public final String food;
public final String sound;
public Pet(String name, String food, String sound) {
this.name = name;
this.food = food;
this.sound = sound;
}
public void eat() {
System.out.println(name + ": Mmmmm, " + food);
}
public void play() {
System.out.println(name + ": " + sound + " " + sound);
}
public void sleep() {
System.out.println(name + ": Zzzzzzz...");
}
public void live() {
new Thread() {
public void run() {
while (true) {
eat();
play();
sleep();
}
}
}.start();
}
public static void main(String[] args) {
new Pet("Fido", "beef", "Woof").live();
}
}

Компилятор найдёт метод с именем sleep в классе Thread и перестанет искать дальше. А так как метод sleep должен принимать параметры, то компилятор тут и ругнётся. Эта проблема называется затенением (shadowing) и описана в спецификции (JLS 6.3.1). Почему так было сделано - история умалчивает. Чтобы программа скомпилировалась можно самому разрезолвить этот метод: Pet.this.sleep(). Но в данном случае лучший совет - не переопределяйте класс Thread, а реализуйте интерфейс Runnable и используйте его для создания нити:
  public void live() {
new Thread(new Runnable() {
public void run() {
while (true) {
eat();
play();
sleep();
}
}
}).start();
}

Великий Пост

Несколько дней назад покупали в Карусели кисель для Мишки. А на нём наклейка, что продукт рекомендуется в великий пост. Я ещё пошутил, что написали бы, что освящено...
И что бы вы думали! Сегодня Юля в магазине купила упаковку морской капусты, на которой круглая наклейка с надписью: Освящено священником Александром из церкви Св. Вмч. Георгия Победоносца в Купчино. Во дают!