среда, 9 октября 2013 г.

Java, ну зачем?



Приветствую, уважаемый читатель.

Когда-то давно на хабре была пара статей как не надо писать на java с интересными задачками по java.

Часть 1, Часть 2

Они очень интересные, но, к сожалению, автор не стал продолжать.


Представляю вашему вниманию еще 2 задачки (на большее не хватило сил. Оказывается, писать статьи не так-то просто.)


В конце статьи, разумеется, будут ответы с разъяснениями, а также дополнительные задания для самых сильных.


Вторая задача взята подчистую из замечательной книги Джошуа Блоха Java Puzzlers. Книга не научит вас, как писать код (скорее наоборот, как не надо и почему), но лично мне было дико интересно это читать. Просто для развлечения. Рекомендую.


Итак, приступим.


Условия




1. Казнить нельзя помиловать


public class A {
public static class X {
public static class Y {
public static String Z = "life is good";
}

public static C Y;
}

public static class C {
public static String Z = "life is pain";
}

public static void main(String[] args) {
System.out.println(X.Y.Z);
}
}




Что произойдет?

  1. Compile error

  2. Runtime error

  3. Выведет life is good

  4. Выведет life is pain


Кроме этого как, не меняя имен, [исправить ошибку, если ответ 1/2 и] вывести обе строчки?
2. Дженерики такие дженерики


public class B {
public static <T> T foo() {
try {
return (T) new Integer(42);
} catch (ClassCastException e) {
return (T) "habr";
}
}
public static void main(String[] args) {
System.out.println(B.<String>foo());
}
}




Что произойдет?

  1. Compile error

  2. Runtime error

  3. Выведет 42

  4. Выведет habr


А теперь правильные ответы:


1. Казнить нельзя помиловать


public class A {
public static class X {
public static class Y {
public static String Z = "life is good";
}

public static C Y;
}

public static class C {
public static String Z = "life is pain";
}

public static void main(String[] args) {
System.out.println(X.Y.Z);
}
}


Выведет life is pain

Тут все дико, но просто. Да, jls такое позволяет. Приоритет всегда у поля.

Гораздо интереснее — как это обойти и вывести-таки life is good? Мне известны три решения:



  • reflection api

  • статический импорт X.Y

  • К статике можно обращаться через экземпляр: (new X.Y()).Z




Задание на пятерку



Последний способ хорош, но требует создания объекта, что плохо. Добавим приватный конструктор.А теперь слабо? (без рефлексии и статического импорта)

public static class X {
public static class Y {
private Y() {}
public static String Z = "life is good";
}

public static C Y;
}


Ответ:
Скрытый текст


((X.Y)null).Z; // подберите свои челюсти, это Java.



2. Дженерики такие дженерики


public class B {
public static <T> T foo() {
try {
return (T) new Integer(42);
} catch (ClassCastException e) {
return (T) "habr";
}
}
public static void main(String[] args) {
System.out.println(B.<String>foo());
}
}


Runtime error

Итак, все мы знаем, что в java дженерики — не более чем синтаксический сахар, ограничивающий наши возможности позволяющий компилятору выполнять дополнительные проверки типов. Но как только программа запущена — вся информация о классах-параметрах теряется. Увы, это не c++ и даже не c#.


Посмотрим внимательно на код:



System.out.println(B.<String>foo());




Компилятор понимает, что тип аргумента — String, а следовательно подставляет наиболее подходящий println:

public void println(String x)




Важно, что определять, какую версию перегруженного метода использовать — задача уровня компиляции, не рантайма.

Идем дальше.



return (T) new Integer(42);




Cast происходит в рантайме, в тот момент, когда дженериков уже «нет».

Что произойдет?

Ничего. Просто вернется Integer.

Ну и приехали — вызвали printf, принимающий String, а передали ему Integer.


Уроки на будущее


  • Не надо так делать

  • В мире есть не только println, принимающий Object

  • Каст к типу дженерика — это костыль




Задание на пятерку



Реализуйте метод, бросающий произвольный Throwable (в том числе, checked exception), не требующий ни thows, ни try-catch:

public static void throwWithoutCheck(Throwable t) {
// Никаких проверок, только хардкор. Хочу throwWithoutCheck(new Exception()) - и никаких throws!
}


Ответ:
Скрытый текст


private static <T extends Throwable> void castAndThrow(Throwable t) throws T {
throw (T) t;
}
public static void throwWithoutCheck(Throwable t) {
B.<RuntimeException>castAndThrow(t);
}



This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Комментариев нет:

Отправить комментарий