...

четверг, 21 ноября 2013 г.

[Из песочницы] Что Java 8 нам готовит

Пройдемся по новинкам и покодируем по-новому.
Итак, начнем по списку.



Допустим имеерся список.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);


Теперь вспомним как мы итерировали в старые времена:



for (int i = 0; i < numbers.size(); i++){
System.out.println(numbers.get(i));
}


Все здорово, но уж очень легко оступиться и поставить <= место < или начать с 1 вместо 0. В вышеприведенном коде мы полностью контролируем механику, мы держим в голове все движущие части. Это хорошо во многих случаях, и никто у нас этого не отобрал в Java 7, только добавили сахарку.




for(int num:numbers){
System.out.println(num);
}


В Java 8, по-прежнему ничего не отбирают, но предлагают «декларативный» подход, значит, мы декларируем что мы хотим сделать, а всю механику — возлагаем на JVM.



numbers.forEach(value -> System.out.println(value));


Заметим что в примере используется лямбда-выражение. О нем в следующем абзаце. Многие уже видели forEach во многих языках и библиотеках. Итак, forEach нам позволяет в одной строке выразить: «вот тебе коллекция, примени-ка данное выражение ко всем ее элементам» (декларативный подход), а не: «начиная с первого, и заканчивая последним, пеербери один за другим и используй каждый элемент».


Теперь о лямбда выражениях.



Очень вкусное нововведение, позволяющее заменить анонимные классы на функции.

рассмотрим пример, уже приведенный выше:



numbers.forEach(value -> System.out.println(value));


если бы не было ламбда-выражений, то даный пример выглядел бы так:



numbers.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}

@Override
public Consumer<Integer> andThen(Consumer<? super Integer> after) {

return null;
}
});


Сложновато, не так ли. Но вернемся к простому однострочному выражению, и здесь все еще можно кое-что упростить: зачем нам упоминать value дважды, если итак ясно, что никакой другий переменной там быть не может.



numbers.forEach(System.out::println);


Да-да мы передали статический метод в качестве функции. Таким образом мы сказали: «вот тебе метод, примени его в цикле».


Вот еще пару примерчиков замены анонимных классов функциями:


Создание потока:



new Thread(SomeClass::runSomething).start();


Сортировка с компаратором



Collections.sort(numbers, (o1, o2)->-o1.compareTo(o2));


Изменения в Collections Framework



Поразмявшись лямбда-выражениями, перейдем к новинкам в Collections Framework. Допустим нам нужно пощитать сумму всех чисел в списке, умноженных на два. Недолго думая, мы создаем кусочек кода:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

int sum = 0;
for (int num : numbers) {
sum += num * 2;
}


Теперь обратимся к декларативному подходу:



int result = numbers.stream().
mapToInt(value->value*2).
sum();




Постойте-ка, не map-reduce ли это? Он самый. Разберем попорядку. Итак mapToInt проводит map операцию, в результате которой элементы становятся умноженными на два. sum — это reduce преобразование, доступное лишь для целых чисел.

Допустим, поступил заказ модифицировать код и обрабатывать лишь четные, а нечетные отфильтровывать.



System.out.println(numbers.stream().
filter(value -> value % 2 == 0).
mapToInt(value -> value * 2).
sum());


теперь наоборот, нечетные щитать, а четные выбросить:



System.out.println(numbers.stream().
filter(value -> value % 2 != 0).
mapToInt(value -> value * 2).
sum());


еще одна новая особенность языка



Мы привыкли что интерфейс нам говорит «что мы будем делать», но ничего не делает сам. В Java 8 интерфейс иногда что-то делает.

Вот вам примерчик:



public interface IFly {
public void takeOff();
public default void cruise() {
System.out.println("IFly.cruise");
};
public void turn();
public void land();
}


Кто программировал на С++ и читал Бьерна Стауструпа, тот задаст вопрос: «а как же diamond problem?». Вопрос интересный. И ответ есть.


Допустим, класс наследует два интерфейса и в каждом из них есть default метод. При компиляции возникнет ошибка.



public interface A {
default void hello() { System.out.println("Hello World from A"); }
}
public interface B {
default void hello() { System.out.println("Hello World from B");
}
public class D implements A,B {}


Разберем другой пример: треугольник



public interface A {
default void hello() { System.out.println("Hello World from A"); }
}
public interface B extends A {
default void hello() { System.out.println("Hello World from B"); }
}
public class C implements B, A {
public static void main(String... args) {
new C().hello();
}
}


В данном случае победит ближайший в иерархии, тоесть интерфейс B. Ну а если хочется использовать метод интерфейса А — то нужно явно указать



A.super.hello();


Разберем третий пример: есть конфликтующий метод у абстрактного класса и интерфейса. Правило — побеждает всегда класс.


Таким образом проблема решена:



  • если конфлик на одном уровне иерархии — компилятор не даст сему случиться

  • если на разных уровнях — победит ближайший

  • если интерфейс против класса — побеждает класс


Еще пару вкусняшек

Base64, как долго компилятор нам выдавал грозные сообщения об устаревшем sun.misc.Base64 и приходилось пользовать апачевские библиотеки для простой, казалось бы, вещи.

теперь вопрос решен: java.util.Base64



Base64.getEncoder().encode(new byte[]{1, 2, 3, 4, 5});


Новый API по работе с датами и временами. Если кому не подуше старый добрый GregorianCallendar, то можете попробовать новые классы Clock, Time, LocalTime



import java.time.LocalDate;
import java.time.Period;
...

LocalDate time = LocalDate.now();

Period period = Period.ofDays(2);
LocalDate newDate = time.plus(period);

System.out.println("year:"+newDate.getYear());
System.out.println("month:"+newDate.getMonth());
System.out.println("day:"+newDate.getDayOfMonth());


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.


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

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