Что 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 5, только добавили сахарку.

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)-> o2.compareTo(o1));


Изменения в 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());
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 147

    +1
    Итераторы доступны со 2 версии.
      +3
      Итераторы во второй, а for(:) только в пятой.
        0
        Был еще Enumeration, тот же итератор по сути, но с 1-й версии. С for(:) не совместим.
          0
          Суть в том что не в седьмой.
        +3
        Классные нововведения, особенно с Collections и лямбдами. Вот только про интерфейсы — как-то неясно, теперь они не такие интерфейсные, чтоль =)
          +3
          Ну, в них по прежнему нету полей (только static final). Больше всего похоже на traits или mixins из других языков.
            +2
            Мне скорее ломает мозг не само введение трейтов — они успешно применяются во многих языках, а то, что не было введено специальное ключевое слово trait. В этом случае default было бы не нужно. Все-таки интерфейс с реализациями — это странно и противоречит многим книжкам по ООП.
              0
              Думаю, что введение нового ключевого слова — это довольно неприятная штука. Поэтому и сделали for(:) вместо for(in) или foreach, @interface вместо annotation и т.д.

              С другой стороны enum ввели в свое время.
                +3
                Добро пожаловать в language design для устоявшихся языков, где для обеспечения обратной совместимости дизайнерам приходится лезть из кожи вон.
                  +3
                  Ну если вспомнить, для чего вообще ввели в жаве эти дефолты, то все станет понятно. А ввели для того, чтоб расширить базовые интерфейсы коллекций лямбда-примочками, не поломав уже написанный код, явно или неявно использующий оные интерфейсы.
                    0
                    Странно возможно, потому что вы java программист и привыкли к такой модели, ясное дело, что в книгах связанных с java и ООП будет перечить, но ООП не такая уж и стандартизированая штука, например взять тот же Objective-C, в нем тоже есть и классы и наследования, но отличий полно и от Java и от C++. Нельзя говорить, какая моделей правильней, но мне лично кажется наличие релизации по умолчанию большим плюсом, это, кроме упомянутых mixins, дает возможность создания Rich Interfaces, где конечному пользователю вашей API не придется реализовать всю кучу методово интерфейса, но реализовать парочку, на которые опирается остальная часть интерфейса.

                    Например
                    interface foo {
                    default void bar() { baz(); }
                    void baz();
                    }

                    Ну и самое главное, все это не приводито к тому аду, что был с множественным наследованием в С++.
                  +4
                  Все хотят немного haskell в себе, а получается java…
                    0
                    Отлично. Рад что Java развивается. Только не понятно одно, зачем давать возможность интерфейсу что-то делать?
                      +1
                      превратить их в trait-ы?
                        +1
                        Если бы они этого не сделали, то все существующие библиотеки коллекций, например, сразу бы потеряли совместимость с J8, так как поменялась сигнатура базовых интерфейсов.
                        +2
                        Поддержка лямбд напрашивалась сама собой. Теперь наконец-то можно будет сделать на Java нормальную реализацию LINQ-подобной обработки данных. Очень удобно!

                        А вот описание тела методов в интерфейсах меня, мягко говоря, удивило. Ведь идеологически интерфейс — это контракт, публичная сторона, которая никак не должна быть связана с конкретной реализацией. По этой причине в C#, например, в интерфейсе нельзя указывать модификаторы доступа или объявлять поля. Зачем смешивать понятия?
                          +1
                          Так вроде появилась LINQ подобная — filter, mapTo. Но всеже так просто сделать свою реализацию LINQ не получится, т.к. в C# это делается еще за счет функций расширения, которыми по сути и является весь LINQ и yield, который позволяет строить быстро итераторы
                            +1
                            Ещё нужны Expression tree которых нет в java для реализации крутых штук типа linq to sql.
                            0
                            В java 8 интерфейсе тоже нельня указывать модификаторы (только public, но все и без него public) и объявлять поля (только static final).
                            +18
                            очень легко оступиться и поставить <= место < или начать с 1 вместо 0

                            Вы, верно, шутите?
                            • UFO just landed and posted this here
                                0
                                можно поглядеть на сгенеренный код, не будет там копирования
                              +1
                              В шарпе отказался от использования List.ForEach. LINQ — хорошо, но List.ForEach — это другое, оно делает код хуже читаемым.
                                +1
                                Cам MS отказался от метода List.ForEach в Windows Store из тех же соображений.
                                  –3
                                  Еще return перестает работать (выходит не из внешней функции, а из самого ForEach)
                                    –2
                                    минус за что?
                                      –3
                                      ок, отсутствие комментариев с минусами для меня означает, что хабр скатился в самое гавно. Можно смело валить отсюда
                                      0
                                      Я в упор не понимаю, за что меня минусуют.

                                      Вот такой код компилируется:

                                      public static int F(List<int> list) {
                                          foreach (var i in list) {
                                              if (i == 0) {
                                                  return 5;
                                              }
                                              Console.WriteLine(i);
                                          }
                                      
                                          return 10;
                                      }
                                      


                                      А вот такой нет:

                                      public static int F(List<int> list) {
                                          list.ForEach(i => {
                                              if (i == 2) {
                                                  return 5; // error CS0127: Since 'System.Action<int>' returns void, a return keyword must not be followed by an object expression
                                              }
                                              Console.WriteLine(i);
                                          });
                                      
                                          return 10;
                                      }
                                      


                                      Это я и хотел сказать. return из F() больше нельзя сделать.
                                        0
                                        Мне кажется это вполне логично, там же return из вложенной функции, с чего бы return должен делаться из F?
                                          –1
                                          А мне кажется более логичным делать возврат из функции F. Вот здесь Neal Gafter объясняет почему. Кстати, такое поведение (возврат из внешней функции) реализовано во многих языках: Ruby, Smalltalk, Scala, Common Lisp
                                            0
                                            Не путаете с генераторами, yield и т.п.?
                                              0
                                              Не путаю.
                                              0
                                              Если вы экземпляр Action<T> из вашего примера передадите снаружи, а не определив его на месте, return в нем тоже должен по вашему завершать объемлющую функцию?

                                              Я не уверен насчет всех языков, но ни в Scala, ни в Python ни в JavaScript, ни в Java оператор return так во вложенных функциях не работает. И не должен ни в коем случае.
                                                0
                                                Вы бы проверили для начала. И ссылку мою открыли. Я знаю, о чем говорю. Scala-код:

                                                def f(list: List[Int]): Int = {
                                                  list.foreach { x =>
                                                    if (x == -1) {
                                                      return 0
                                                    }
                                                
                                                    println(x)
                                                  }
                                                  
                                                  list.sum
                                                }
                                                


                                                Вызов f(List(-1,0,1)) возвращает 0.

                                                Если же я лямбду передам наружу и там её вызову, то вылетит ошибка.
                                                  0
                                                  Ладно, насчет Scala — да, там свой хитрый оператор return ибо язык функциональный + функция есть выражение (да и вообще return использовать не рекомендуют, рекомендуют использовать правило последнего оператора).

                                                  Но это не меняет того факта что в императивных языках с функциональными элементами такое поведение абсолютно нелогично, потому что тело функция может определять произвольную последовательность действий, а не «формулу» для получения результата.
                                                    –1
                                                    Хватит бросаться словами «абсолютно нелогично» и «не должен ни в коем случае». Это просто ваше мнение. Если я заменяю цикл foreach на вызов метода ForEach (или наоборот), то мне бы хотелось иметь то же самое поведение метода return. Иначе мой код просто перестанет работать.
                                                      +1
                                                      Что делать если я хочу просто выйти из функции анонимной? А меня выкинет из той, в которой эта анонимная функция вызывается…?
                                                        –1
                                                        Я могу задать вам обратный вопрос. Что делать, если я хочу выйти из внешней функции? Понимаете, тут tradeoff между консистентностью кода (код с closure должен работать так же, как и код без closure) и привычностью/ограничениями конструкций языка. Оба решения имеют свои плюсы и минусы.

                                                        PS. Если уж совсем приспичило выйти из анонимной функции, то можно использовать goto
                                                          –1
                                                          Используйте goto, пожалуйста, в языках, где изначально предполагалось именно излюбленное вами поведение. Если в джаве return начнет себя вести по другому, то где же хваленая обратная совместимость?
                                                        0
                                                        > Если я заменяю цикл foreach на вызов метода ForEach (или наоборот), то мне бы хотелось иметь то же самое поведение метода return.

                                                        Почему? Вы заменяете конструкцию языка на вызов метода принимающего функцию как параметр. Почему вы ожидаете одинакового поведения?

                                                        Насчет Scala я ошибся, прошу прощения, я редко пользуюсь return-ом в этом языке. В Scala это выглядит более логичным, потому что функция = выражение, выражение = функция. Поэтому ожидать подобного поведения можно (что собственно и описано по приведенной вами ссылке). Но проецировать подобное поведение, характерное для функциональный языков на языки с доминирующей императивностью — неправильно.
                                        +1
                                        А методы расширения не планируют вводить?
                                          0
                                          вот убить бы за default реализацию интерфейса…
                                            +1
                                            Что не так?
                                              +1
                                              обоснуйте
                                                0
                                                потому что 1) это ломает изначальную концепцию интерфейса, как контракта, 2) Это могут и будут использовать неправильно, 3) Теперь это не интерфейс, а абстрактный класс.
                                                  +5
                                                  Не совсем, интерфейсы не могут иметь состояние и я не согласен что это ломает концепцию интерфейса как контракта.

                                                  И да, действительно, это могут использовать неправильно, однако, как говорится: «with great power comes great responsibility».
                                                    +1
                                                    2) Это могут и будут использовать неправильно

                                                    Кому надо, тот будет пользоваться правильно. А быдлокодеры, не умеющие применять ООП, пусть и дальше не умеют его применять.
                                                  –2
                                                  Согласен. Чем тогда он будет отличаться от абстрактного класса? Множественным наследованием? Так ведь изначально от этого осознанно ушли. И опять вернулись. Тоже не поддерживаю это нововведение. Еще ни разу не было необходимости в дефолтных реализациях методов интерфейса.
                                                    +4
                                                    Они были нужны как минимум чтобы расширить интерфейсы из стандартной библиотеки. Map, Set и т. д.
                                                      –1
                                                      А что мешает унаследоваться от этих интерфейсов? Сделать абстрактный класс с необходимой реализацией. При грамотном проектировании таких проблем не возникает.
                                                        +4
                                                        Мешает то, что уже есть тысячи реализаций этих интерфейсов, которые никто переписывать не будет. Жаль, что когда проектировали эти интерфейсы в 1997-98 году, вы не подсказали им, как сделать грамотно.
                                                          –1
                                                          Зачем же на личности переходить. И не пойму, как мешают тысячи уже готовых реализаций сделать свою
                                                            0
                                                            Зачем нужны новые фичи, чтобы использовать которые, нужно перелопачивать весь старый отлаженный код. Проще уж тогда обойтись без лямбд в коллекциях. Сейчас же как-то обходятся.
                                                              0
                                                              Объясняю: было принято решение добавить новые методы в существующие классы в JDK. Пусть это будет метод foo в интерфейсе Collection.

                                                              Если просто добавить foo в Collection, то вы (программист на Java) больше не сможете скомпилировать свой код, если где-то в вашем коде есть классы, реализующие любую коллекцию. Потому что у них метода foo. Вам придётся имплементировать метод foo в ваших реализациях. Поэтому и придуманы дефолтные реализации методов.

                                                              Предложите другие решения.
                                                                0
                                                                То есть, это такой хак, который позволит обратную совместимость обеспечить, а не нововведение, которое кому то там жизнь облегчит.
                                                                  0
                                                                  Как всегда и то и то :) В моём личном понимании — скорее хак, да. Было даже обсуждение, оставить этот хак приватным, то есть разрешить его использовать только в системных пакетах. Потом решили, что другим (прежде всего, разработчиками бибилотке и вообще любых API) это тоже может быть интересным, и сделали фичу публичной.
                                                        +2
                                                        У меня была такая необходимость, иметь реализацию интерфейса, чтобы вызвать в каком-то нужном порядке методы этого интерфейса, иначе приходилось дублировать код во всех местах.

                                                        Например:

                                                        interface Car {
                                                            void bump();
                                                            void beep();
                                                        
                                                            void run(){ // этот код приходилось реализовывать везде где есть Car
                                                                bump();
                                                                beep();
                                                            }
                                                        }
                                                        
                                                          0
                                                          Так вроде если есть желание создать в интерфейсе поведение — сделай абстрактный класс.
                                                            +2
                                                            Ан нет, т.к. класс, который имплементирует Car уже наследуется от другого абстрактного класса, в который мы не можем добавить Car. Или этот абстрактный класс уже реализован в библиотеке, менять который нельзя/невозможно.
                                                              0
                                                              Налицо проблема в проектировании :)
                                                                0
                                                                Рискну предположить, что это могло быть сделано другой командой 2 года назад в библиотеке код которой модифицировать нельзя и нельзя отказаться от ее использования
                                                                0
                                                                Унаследовать новый абстрактный класс от старого, имплементировать в нем нужный метод и наследовать свои классы уже от него.
                                                                  +2
                                                                  Плохое решение: вы избавляетесь от методов в интерфейсе, зато ваш абстрактный класс (до которого может быть не один, не два и не пять уровней наследования) приобретет методы, которые к нему, скорее всего, вообще отношения не имеют. А заодно этот метод получат все классы из других ветвей, которые имели несчастье иметь того же предка.
                                                                    0
                                                                    Не совсем понял мысль.
                                                                    Мне нужно дефолтную реализацию метода. У меня есть абстрактный класс A,. я наследую от него абстрактный класс B и реализую в нем метод run. В конкретном классе C, унаследованном от B я уже буду иметь эту «дефолтную реализацию».
                                                                    Какой то другой класс, наследующийся от A, получит сайд-эффект?
                                                                      0
                                                                      Если C наследуется от B не напрямую, а, например, через D, то метод из интерфейса получат и D, и все его наследники, а не только C, что есть нехорошо.

                                                                      Кроме того, такой подход вообще не работает для ситуации, когда интерфейс имплементируют классы из совершенно разных иерархий, у которых единственный общий предок — Object. У вас по-прежнему в каждом дереве наследования будет своя копия кода для run(), и если в какой-то момент потребуется изменить поведение этого метода — изменения придется вносить во все имплементации. Методы в интерфейсах позволяют вообще выпилить подобную копипасту из проекта.

                                                                        0
                                                                        Откуда возьмется «через D»? Только что C наследовался от A и я специально между ними вклиниваю B.

                                                                        А эта архитектура с множественными глубокими иерархиями, где реализация интерфейса висит на верхушке, на конкретном классе — нежизнеспособна.
                                                                          0
                                                                          Откуда возьмется «через D»

                                                                          Оттуда же, откуда и A,B и C. Вот взял программист и построил дерево наследования
                                                                            0
                                                                            Никак не привыкну к хаброшорткатам.

                                                                            Вот взял программист и построил изначально дерево наследования
                                                                            abstract A -> D -> C
                                                                            И имплементировал в C некий интерфейс. Следуя вашей логике, нужно ввести новый абстрактный класс B между A и D, и туда поднять имплементацию интерфейса — на два уровня вверх, где этот контракт вообще не нужен.

                                                                            с множественными глубокими иерархиями

                                                                            Два уровня наследования — это уже глубокая иерархия?
                                                                              0
                                                                              Зачем вставлять на два уровня выше, это действиетльно вопрос, я предлагал на один уровень.
                                                                                0
                                                                                И будет тогда решение для одного-единственного частного случая — если все имплементации интерфейса в проекте имеют одного и того же непосредственного родителя.
                                                                                Остальные случае не покрыты.
                                                                      0
                                                                      И какой толк? Код run() все равно придется скопировать.
                                                                +5
                                                                > Чем тогда он будет отличаться от абстрактного класса? Множественным наследованием?

                                                                Отсутствием состояния.

                                                                > Так ведь изначально от этого осознанно ушли.

                                                                Множественное наследование является злом при наличии состояния. При его отсутствии, все становится намного проще, почему его и вернули.

                                                                > Еще ни разу не было необходимости в дефолтных реализациях методов интерфейса.

                                                                Необходимости на самом деле нет ни в классах, ни в циклах (все можно написать на if/goto и глобальных переменных). Речь об удобстве. Посмотрите еще раз, насколько богаче стали новые интерфейсы коллекций, и насколько удобнее их стало использовать. Теперь представьте себе, что было бы, если бы для каждой конкретной реализации коллекции её автору пришлось бы реализовывать все эти методы самому с нуля.
                                                                  +1
                                                                  Спасибо за развернутый ответ!
                                                                  У меня при вопросе множественного наследования всегда больше всего вызывало затруднения определить, какой метод будет вызван. Джава мне тем и нравится, что все строго и понятно. Вот интерфейсы, вот классы. Нужна дефолтная реализация или базовое состояние — юзай абстрактный класс. Нужен контракт — юзай интерфейс. Боюсь, что это значительно все усложнит :(
                                                                  Хотя может быть я и напрасно переживаю, просто непривычно. А как привыкну — станет легко и понятно :)
                                                                    +1
                                                                    Компилятор будет ругаться если непонятно и потребует переопределить метод явно.
                                                                  +3
                                                                  interface Ordered<T> {
                                                                    int compareTo(T other);
                                                                  
                                                                    default boolean greater(T other) {
                                                                      return this.compareTo(other) > 0;
                                                                    }
                                                                  
                                                                    default boolean less(T other) {
                                                                      return this.compareTo(other) < 0;
                                                                    }
                                                                  
                                                                    default boolean greaterOrEqual(T other) {
                                                                      return this.compareTo(other) >= 0;
                                                                    }
                                                                  
                                                                    default boolean lessOrEqual(T other) {
                                                                      return this.compareTo(other) <= 0;
                                                                    }
                                                                  }
                                                                  


                                                                  Теперь берем любой класс и подмешиваем в него Ordered, реализуем compareTo и сразу имеем 4 дополнительных полезных метода, которые не надо вручную реализовывать.
                                                                +3
                                                                Более полный обзор со ссылками: www.techempower.com/blog/2013/03/26/everything-about-java-8/
                                                                  +11
                                                                  они изобретают С#!
                                                                    +1
                                                                    Скорее возвращаются к истокам:

                                                                    c := #(1 2 3).
                                                                    x := c ((collect: [:item | item + 1]) select: [:item | item > 2]) inject: 0 into: [:acc :item | acc + item].
                                                                      0
                                                                      Вы со скобками немного напутали — между объектом и вызовом метода скобки ставить нельзя.
                                                                        0
                                                                        Чёрт, а я и думаю, что-то тут не то. Спасибо.
                                                                      +2
                                                                      А C# когда-то изобрели с оглядкой в т.ч. на Java, и это нормально. А по сабжу — все эти прелести дойдут до боевого EE ещё очень нескоро, печаль.
                                                                        –1
                                                                        Скорее Scala. Осталось добавить автоматический вывод типов и неявные функции и разницы между ними почти не останется.
                                                                          –3
                                                                          В C# нету default методов
                                                                            0
                                                                            Что в C# можно в интерфейсе указывать реализацию метода?
                                                                          +6
                                                                          Вопрос «чем отличается абстрактный класс от интерфейса?» получил +1 к убойной силе и +5 к троллингу :)
                                                                            +4
                                                                            Заклинание «в интерфейсах не может быть конструкторов» отрезистит этот вопрос. ;)
                                                                              +6
                                                                              И полей.
                                                                                +5
                                                                                Чем полить?
                                                                                  0
                                                                                  И множественное наследование опять же
                                                                              +1
                                                                              Я ни разу не джавист, но у меня есть, возможно глупый, вопрос относительно реализации методов по-умолчанию в интерфейсах. Это фича только языка Java? Или разработчики добавили какие-то дополнительные конструкции в байткод для её реализации?
                                                                                –1
                                                                                Может и добавили, но реализуется это на уровне языка уже давно в той же Scala.
                                                                                  0
                                                                                  Там invokedynamic появился. И ещё methodHandler и AnonimousClassLoader.
                                                                                    0
                                                                                    Ну зачем минусовать человека?
                                                                                    Лямбды действительно используют invokedynamic.

                                                                                    Тем не менее, как справедливо заметили выше, можно было бы обойтись и без этого.
                                                                                    Так же, думаю, код может завестись и на старых JVM при передаче соответсвующего значения через флаг -target во время компиляции.
                                                                                      0
                                                                                      Поправочка: реализации JDK от Oracle и OpenJDK в даный момент действительно используют indy. Другие реализации вольны делать всё, что им угодно. Например, в процессе компиляции заменять лямбды на семантически эквивалентные конструкции: генерировать статические методы в классе, где используется лямбда или генерить анонимные иннер-классы. И т.д. в том же духе.
                                                                                  +3
                                                                                  > очень легко оступиться и поставить <= вместо < или начать с 1 вместо 0

                                                                                  О да, а теперь вообще ни разу не оступишься

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

                                                                                  (я про ->-)
                                                                                    +1
                                                                                    Ну только сейчас это синтаксическая ошибка, которую увидит компилятор (в отличие от <= вместо <).
                                                                                      0
                                                                                      Вы извините, если я сейчас глупость сморожу, я об этих вкусностях Java 8 почти в первый раз слышу, и как и многие читатели топика, жажду узнать больше. Я в топике вижу -> и ->-, соответственно обе их компилятор глотает, и ошибится между ними можно так же как и <= и <.
                                                                                        +1
                                                                                        Collections.sort(numbers, (o1, o2)->-o1.compareTo(o2))

                                                                                        тоже самое что

                                                                                        Collections.sort(numbers, (o1, o2) -> -o1.compareTo(o2))
                                                                                        (перед вызовом compareTo на o1 стоит унарный — (минус)).
                                                                                          +1
                                                                                          Спасибо за разьяснение, теперь понятно.

                                                                                          PS: разделять разные операторы хотя-бы пробелами — хорошая привычка :)
                                                                                            0
                                                                                            сорики что запутал, исправлено.
                                                                                            +1
                                                                                            И, кстати, использование унарного минуса — не очень хорошая практика в данном случае, лучше написать
                                                                                            o2.compareTo(o1)
                                                                                            для более простого чтения кода. Хотя это скорее вопрос соглашений в компании.
                                                                                              +2
                                                                                              Да действительно, но тут появилась другая проблема.
                                                                                              Отличить o2 от o1 не так то просто. Нужно их еще переименовать для лучшей читаемости.

                                                                                              А еще лучше, наверное:
                                                                                              Collections.sort(numbers, Ordering::reverse).
                                                                                      –8
                                                                                      дайте структуры и unsigned! в топку эти лямбды. А еще честные дженерики, а не этих уродцев
                                                                                        –1
                                                                                        А что мешает сделать структуру из класса? (Вы ведь подразумеваете аналог struct в С++?)
                                                                                          0
                                                                                          То что она создестя на куче а не в стеке не важно? Если нет то все ок. Но автору коментария (и многим кому приходится оптимизировать работу с памятью) важно.
                                                                                            +2
                                                                                            В С++ ключевое слово struct имеет другой смысл в отличие от C#. В С++ структуры фактически являются класами только с public полями по умолчанию.
                                                                                              +1
                                                                                              Да и структуру и класс (это одно и тоже вообщем) можно создать как в куче так и на стеке. Эта приятная особенность С++ мне известна. Я намекал SVLad о том что пользователи намного чаще хотят C#-style структур в Java. Особенно те кто пытается снизить нагрузку на GC.
                                                                                                +1
                                                                                                А, теперь понял. А на каких проектах с производительностью всё так плохо, что выметание таких вот одноразовых объектов из первого поколения уже критично?
                                                                                                  0
                                                                                                  Предполагаю игры и работа с графикой.
                                                                                                    +1
                                                                                                    Любой где количество таких объектов очень большое. Например — процессинг квот в биржевом софте.
                                                                                          +5
                                                                                          numbers.forEach(System.out::println);
                                                                                          Да-да, мы передали статический метод в качестве функции.

                                                                                          Мы передали обычный метод статической переменной «out».

                                                                                            0
                                                                                            А я это прощёлкал. Означает ли, что запись в нижеследующем стиле будет валидной?

                                                                                            MyFormatter f = «Hi, %s»;
                                                                                            numbers.forEach(f::format);

                                                                                              0
                                                                                              Похоже на то, т.к. System.out это объект, т.е. получается мы получаем метод объекта, а не класса, а получаем ли this в этом методе?
                                                                                                0
                                                                                                Нотация из топика и нотация в моем комментарии одинаковы (если пропустить инициализацию строкой:) ). Вопрос — как передаются тогда статические методы?
                                                                                                  0
                                                                                                  Наверно также, только вместо объекта имя класса…
                                                                                              0
                                                                                              Спасибо, что подметили. Я исправил.
                                                                                              +1
                                                                                              В данном случае победит ближайший в иерархии, то есть интерфейс B. Ну а если хочется использовать метод интерфейса А, то нужно явно указать

                                                                                              A.super.hello();


                                                                                              A.hello() или B.super.hello() вроде бы, или я что-то путаю?
                                                                                                0
                                                                                                Тоже не понял этого выражения.
                                                                                                +1
                                                                                                Base64, как долго компилятор нам выдавал грозные сообщения об устаревшем sun.misc.Base64 и приходилось пользовать апачевские библиотеки для простой, казалось бы, вещи.

                                                                                                Всегда использовал javax.xml.bind.DatatypeConverter, зачем апачевские библиотеки?
                                                                                                  +2
                                                                                                  Жалко только, что пока эти все плюшки дойдут до Android — я уже состариться успею :)
                                                                                                  Ну а вообще, конечно неплохо бы убирать лишнюю писанину как в Scala, когда можно написать var и тип выведется по выражению справа. Также и; — уже давно ненужный элемент языка. Я бы этого очень хотел для любимого языка — большей выразительности, меньше кода, при этом читабельность на уровне.
                                                                                                  А плюшки Java 8 — гуд, особенно Streams.
                                                                                                    0
                                                                                                    Особенно параллельные streams.
                                                                                                      0
                                                                                                      Особенно сортировка потенциально бесконечного stream'а :-)
                                                                                                      –1
                                                                                                      На мой взгляд var и всякие похожие фичи делают код плохо читаемым. Не понятно какого типа переменная, намного сложнее искать ее создание, особенно если оно скрыто за кучей методов. Поэтому я категорически против такого.
                                                                                                        +3
                                                                                                        Опыт C# показывает, что как раз наооборот var делает код более читаемым. Ведь и так понятно var x = new SomeObject(); что x будет типа SomeObject. Так же если создается фабрикой, от фабрики примерно ясно, что за объект. Вот в случаях когда не ясно по коду, какой тип возвращает функция, тогда тип, конечно, желательно указывать явно для повышения читаемости кода.
                                                                                                          –1
                                                                                                          ничего не понятно в случае: var x = MyClass::getSomeMagicMethod(); А если таких вызовов много, приходится постоянно лезть внутрь этих методов.
                                                                                                            +2
                                                                                                            Var очень удобен для каких-нибудь мудреных превращений коллекций в пределах метода. В тех же лямбдах. Ну т.е. как и всегда, эта конструкция где-то оправдана, а где-то только запутывает.
                                                                                                            –2
                                                                                                            Зачем мне для читаемости кода знать тип переменной? Это лишняя информация.
                                                                                                              +1
                                                                                                              var x = point.getX()+2;
                                                                                                              var y = point.getX()-3;
                                                                                                              if(x==y) {
                                                                                                                //...
                                                                                                              }
                                                                                                              


                                                                                                              Если get'ы возвращают целый тип, то все хорошо, если дробный — у вас тут кучка потенциальных багов, связанных с точностью вычислений. Не зная типов, вы, скорее всего, пропустите это место — в самом деле, сложили, отняли и сравнили, где тут может быть баг?
                                                                                                                –1
                                                                                                                Да, есть такая проблемка. Но, вот например, в языке SML оператор == не определен для дробных чисел. Там будет ошибка компиляции. Т.е. тут проблема не в выводе типов, а в самом операторе ==.
                                                                                                                  0
                                                                                                                  Согласен. То, что при интуитивном подходе порождает баги, должно стать неинтуитивным. :-) Right way.
                                                                                                                –1
                                                                                                                Меня, скорее всего, минусуют те, кто пишут настолько хуевый код, что им приходится указывать тип всех переменных, чтобы понять его. Потому что я отлично читаю код и без явного указания типов. Я что особенный какой-то?
                                                                                                                  0
                                                                                                                  Когда код пишешь, проблем с типом переменных нет. А вот когда заходишь в чужую функцию во время отладки, явный тип переменных помогает.
                                                                                                                    0
                                                                                                                    Я вам еще раз повторяю. Убираете типы переменных, пишете нормальный код с нормально именованными методами и переменными, и весь код отлично читается. Если вы без явного типа переменных не можете понять код, значит он написан криво.

                                                                                                                    А в любом нормальном отладчике тип переменной всегда можно посмотреть при необходимости.
                                                                                                              0
                                                                                                              В динамических языках вообще типы нигде не указываются. Там функции могут возвращать все что угодно. И что — люди справляются же.
                                                                                                              0
                                                                                                              Вместо var можно использовать продвинутую IDE, которая сама подставляет тип переменной при объявлении. При этом в коде будет видно, что за тип переменной используется.
                                                                                                                0
                                                                                                                Это все равно не реализуется в системах ревизии кода, когда ты проверяешь код, то видишь var, но не видишь тип.
                                                                                                                  0
                                                                                                                  Я же об этом и говорю. Автоподстановка типа от IDE позволяет всегда видеть тип переменной и при этом вообще не нужно набирать тип переменной самому.
                                                                                                              –6
                                                                                                              чувствую запах перла…
                                                                                                                +1
                                                                                                                Может, было бы правильнее использовать в статье слово «функциональный» вместо «декларативный»?
                                                                                                                  0
                                                                                                                  Не соглашусь. Декларативный — значит пишем код и говорим что мы хотим сделать, а не как именно. Когда мы итерируем коллекцию и в цикле что-то делаем — мы определяем механику перебора. Используя декларативный подход — то говорим: примени метод/функцию/выражение ко всем элементам.
                                                                                                                    0
                                                                                                                    Посмотри как пишется код на функциональных языках (я лично непосредственно имел дело только с F#, но это вполне себе типичный функциональный язык) и на декларативных (QML, Prologue, HTML :) ) и поймешь, что таки в статье используется именно первый вариант. Хотя википедия говорит, что «декларативный ЯП» имеет 2 значения, одно из которых — любой неимперативный язык.
                                                                                                                  0
                                                                                                                  Сортировка потенциально бесконечного stream'а — вот что страшно…
                                                                                                                    0
                                                                                                                    Итератор тоже может быть «потенциально бесконечным». И что?
                                                                                                                      0
                                                                                                                      На нем нельзя вызвать sort.
                                                                                                                      А на stream — можно.
                                                                                                                        0
                                                                                                                        Конечно для безконечного множества никто стабильной сортировки гарантировать не может (только если это не уже заведомо отсортированное множество).

                                                                                                                        Что нам говорит API:
                                                                                                                        For ordered streams, the sort is stable. For unordered streams, no stability guarantees are made.

                                                                                                                        Похоже момент оговорен. Я думаю (не исследовал) что сортировка происходит по частям, что и не гарантирует общей правильности результата.
                                                                                                                    0
                                                                                                                    В данном случае победит ближайший в иерархии, то есть интерфейс B
                                                                                                                    Почему B? Ведь C реализует и A тоже.
                                                                                                                      0
                                                                                                                      а ещё 8-ка преподносит «сюрприз» в виде исправления давнего бага, который лично на моей тачке выявился в смене результата System.getProperty(«user.home) с „D:“ на реальный %userprofile%…

                                                                                                                      Only users with full accounts can post comments. Log in, please.