0,0
рейтинг
3 августа 2013 в 15:13

Разработка → Любовь и ненависть к Java 8 из песочницы

Java*
Похоже Java 8 самый ожидаемый релиз всех времен. Изначально планирующий релиз на сентябрь прошлого года, перенесли на март следующего года, предположительно для того, что бы потратить больше времени на доработки безопасности, в основном направленные на клиентскую часть Java (JavaFX/Swing).

Новая версия Java пытается “совершенствоваться” так, как понимает это слово Microsoft. Это означает кражу большой части вещей, о которых заботились другие фреймворки и языки, затем включение их в язык или runtime. В преддверии нового релиза, сообщество Java обсуждает Project Lambda, stream, functional interfaces и другие плюшки. Так давайте рассмотрим что хорошо, а что мы можем возненавидеть.

Stream


Основное нововведение это коллекция, называемая Stream, не путайте с InputStream и OutputStream. Stream не замещает ArrayLists или другие коллекции. Это нововведение позволяет управлять данными быстрее и легче. Stream — это одноразовый объект, т.е. обработать данные в нем можно один раз.

Stream обладает возможностью применить функции filter, map, reduce для его обработки. Для Stream есть два режима: последовательный и параллельный. Это позволяет задействовать возможности многоядерных процессоров. Коллекции используют fork/join параллелизм для разбиения работы на части.

Для последовательного режима:
List <Person> people = list.getStream.collect(Collectors.toList());


Для параллельного режима:
List <Person> people = list.getStream.parallel().collect(Collectors.toList());


Во время последовательной обработки Stream, каждый элемент обрабатывается по очереди. А в параллельном режиме массив разбивается на части, каждая из которых обрабатывается в отдельном потоке. Затем результаты обработки собираются в общий результат.

Обработка в параллельном режиме выглядит так:
List originalList = someData;
split1 = originalList(0, mid);
split2 = originalList(mid,end);
new Runnable(split1.process());
new Runnable(split2.process());
List revisedList = split1 + split2;


Stream может быть обработан только раз, и он возвращает другой Stream, поэтому для получения полезного результат можно применить окончательную (terminal) функцию. Например, функции sum(), collect(), toArray(). Пока к Stream не применена окончательная функция, результат обработки не вычисляется. Например:

Double result = list.getStream().mapToDouble(f -> f.getAmount()).sum();
List<Person> people = list.getStream().filter(f -> f.getAge() > 21).collect(Collectors.toList());


Большая польза от этого – возможность использовать несколько процессорных ядер для обработки коллекции. Вместо использования традиционного for-цикла, использование Stream в параллельном режиме теоретически ускоряется, с увеличением числа ядер, задействованных в обработке. Основная возможная проблема это потеря читаемости кода при большом числе операций производимых над коллекцией. Другие проблемы возникают из-за добавления поддержки новых возможностей – функциональные интерфейсы и лямбды.

Functional Interfaces


В целом это просто добавление default-методов в интерфейс с возможностью их прямого вызова из интерфейса. Так же их не обязательно переопределять в реализации интерфейса.

Это было сделано для обратной совместимости с вашими коллекциями в ваших интерфейсах. Т.е. решение проблемы помещения Stream в интерфейс без необходимости изменять все реализующие этот интерфейс классы. Поэтому, создание default-метода в интерфейсе позволяет всем реализующим интерфейс классам использовать потоки. Если default-метод не корректен, то он может быть перегружен.

По существу default-методы это форма множественного наследования. И это становится проблемой того, кто реализует интерфейс, т.к. ему всё равно потребуется переопределить метод. Так же реализующий интерфейс может выбрать, какой базовый метод (supermethod) использовать, это означает что большинство классов реализующий интерфейс могут измениться.

Об этой детали в Java 8 беспокоится много людей. Возможно, это не побеспокоит тех, кто знаком с языком Scala. Функциональные интерфейсы можно напрямую сравнить с концепцией trait-ов в Scala. Однако есть несколько различий: функциональные интерфейсы в Java 8 не могут получить ссылку на реализующий класс, однако Scala позволяет это с помощью ключевого слова self. Зануды могут возразить, что в Java 8 функциональные интерфейсы разрешают множественное наследование поведения, но запрещают наследование состояния, в то время как, в Scala разрешается и то, и то.

Lambda


Назначение лямбда-выражений – более изящный код. При компиляции лямбда-выражение преобразуется в функциональный интерфейс. Вот пример, где анонимный класс заменен на лямбда-выражение.

Старый стиль:
button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent ae) {
      System.out.println(“Action Detected”);
    }
  }
);


Новый стиль:
button.addActionListener(e -> {
        System.out.println(“Action Detected”);
    }
);


И еще один пример.
Старый стиль:
Runnable runnable1 = new Runnable() {
@Override
public void run() {
        System.out.println("Running without Lambda");
    }
};


Новый стиль:
Runnable runnable2 = () -> { System.out.println("Running from Lambda"); };


Как вы можете видеть, использование лямбда-выражений делает код более читабельным, и он короче. Это взволновало много людей в около Java сообществе. В Scala уже есть все эти возможности. И не сюрприз, что Scala сообщество настроено скептически, потому что много нововведений в Java 8 выглядят как замена оператор => на -> в Scala. В некоторых случаях синтаксис Java 8 выглядит более многословным или менее чистым, чем в Scala. Пока не ясно, будет ли все так, как в языках на подобии Scala, построенных на лябда-выражених.

С одной стороны, если Java продолжит развиваться и реализовать все вокруг лябмда-выражений, как сделали в Scala, тогда возможно не будет надобности в Scala. С другой стороны, если лябмда-выражения будут только для обеспечения основной функциональности, вроде помощи с анонимными классами, тогда Scala и другие языки продолжат процветать, как они делают сейчас. Это лучший выход, это позволит другим языкам расти и быть изобретательными без беспокойства о том могут ли они стать устаревшими.

Java time


У Java долгая история, связанная со временем. Сначала был класс java.util.Date, который быстро показал, что Sun может объявлять методы устаревшими в рекордное время, но эти методы могут остаться навечно. И не забудьте java.sql.Date, который помогал узнавать время и место для использования fully qualified names (FQNs) в коде.

Потом это был Calendar, который осознал, что код может работать, более чем с одной частью света в одной JVM. Но работа с временем и датами требовала большого числа monkey-кода и возни с разрозненным API. Поэтому рождались сторонние библиотеки, такие как JodaTime. Теперь Java, с опозданием, решила навести порядок в пакете java.time. Для меня это выглядит как API для работы со временем, который мы всегда хотели.

Nashorn


Netscape создали технологию, называемую LiveScript, позволяющую работать со скриптами на их веб сервере. Было решено портировать её в их браузер, и потребовалось более красивое имя, так что LiveScript был лицензирован под торговой маркой Java от Sun и назван JavaScript – что посодействовало путанице относительно связи между Java и JavaScript. Однако, после распада компании AOL, некоторые члены команды Netscape продолжили реализовывать план Netscape по переписыванию браузера на Java. Для того что бы это сделать, было необходимо реализацию JavaScript в Java, Netscape назвал этот проект Rhino.

Современный JavaScript, этот не тот JavaScript, который знал твой отец. Он может быть полезен как на клиентской стороне, так и на серверной, и вы можете разрабатывать приложения, которые читабельные и быстрые. В JDK 7 добавили invokeDynamic — поддержку динамических языков. А В JDK 8 предоставят более полезную реализацию JavaScript и что, возможно, сделает Nodyn (Red Hat's port of Node.js to the JVM) не очередной жуткой поделкой. Вообще у Oracle есть своя реализация Node.js, креативно названная Node.jar. В чем уверено большинство людей, так это в том, что они хотят запускать всякие штуки на JVM, но не хотят использовать для этого синтаксис Java.

Есть места, где полезен запуск JavaScript из Java. Например, можно использовать client-side validator, как server-side validator, т.е. иметь один и тот же код, работающий в двух местах. Иметь свой собственный Node.js вместе с Java — это как обзавестись милым монстриком, кто не хочет такого? Если читая этот текст, вы не уверены, серьезен я или нет, то это делает нас похожими.

Accumulators


Сначала был synchronized. Однако, если все что вам нужно делать это увеличивать счетчик из многих потоков, то synchronized тяжеловат для этой задачи. Он стал не такой тяжелый в Java 6, сделав неисчислимые блокировки дешевле. В основном это помогло старым приложениям, все ещё использующим Vector, это однопоточный хлам, который поразил каждую библиотеку в Java Activation Framework.

С появлением пакета java.util.concurrent стало лучше — пул потоков и другие сложные многопоточные конструкции, но если все, что вы хотите это просто увеличение счетчика потоками, это все было излишне. Для этого нам были даны atomic-и — быстрые и легче, чем настоящие блокировки. Однако Doug Lea и его любимая армия студентов выпускников еще не закончила. В JDK 8 нам дадут accumulators и adders. Они более легкие, чем atomic-и, и с ослабленными гарантиями, это то, что больше всего нужно параллельному коду, увеличивающему общий счетчик. Ожидаю увидеть это нововведение в реализациях map/reduce. Однако вам все еще нужны atomic-и, если вы хотите читать значение счетчика в потоках, так как порядок аккумулирования счетчика не гарантирован.

Исправления HashMap


Существует известный баг, связанный с тем, как String.hashCode() реализован в Java. Если большое число параметров имеют одинаковый хеш, это вызовет чрезмерную нагрузку на CPU при работе с HashMap. Такая ситуация может возникнуть, если приложение подвергнется denial-of-service атаке, как в этом случае.

Сейчас, корзины в HashMap используют связанный список для хранения значений. Если есть большое число коллизий, тогда сложность работы со структурой изменяется от O(1) до O(N). Теперь при достижении определенного числа элементов в корзине, корзина переключится на использование сбалансированного дерева, что снижает сложность до O(log n).

TLS SNI


SNI — это не имя персонажа Dr. Seuss, а Server Name Identification. Все любят SSL или TLS, или как это теперь называется. Много сайтов используют один и тот же IP и name-based virtual host. Что означает, что вторая строка HTTP запроса это имя хоста. Я могу сделать запрос на podcastd.infoworld.com и www.infoworld.com, находящиеся но одном и том же IP, но получить разные страницы, из-за разного имени хоста. Однако я не могу держать много сайтов на одном IP из-за SSL. Для каждого SSL сертификата я должен иметь отдельный IP адрес. А если вспомнить печальную ситуацию с нынешним числом IP адресов в IPv4, то все становится еще печальнее.

Но теперь Java поддерживает SNI. Большинство современных браузеров поддерживает SNI, Apache поддерживает и Java теперь тоже поддерживает. Это означает, то чего мы так долго ожидали — Tomcat и другие основанные на Java серверы, использующие медленную реализацию SSL от Oracle (JSSE), теперь поддерживают SNI.

Заключение


В общем, вкусные плюшки ожидают в Java 8, но тут же затаились и грабли. По-моему мнению streams лучше дополнение. Надеюсь, параллелизм коллекций увеличит скорость их обработки. Перенеси свой набор данных в память, и когда понадобится извлечь что-то из этих данных, запусти streams в параллельном режиме и получи необходимые данные. То, что вызывает опасения, так это функциональные интерфейсы. При не правильном использовании может вызывать кучу головной боли.

От переводчика


Это перевод вот этой статьи
Увидел в комментариях желание хабровчан узнать про плюшки Java 8, да и сам хотел, и тут попалась эта статья, так что вот сел и перевел. Перевод не дословный, но я постарался сделать как можно ближе к тексту, нежножко пришлось выкинуть, т.к. автор статьи шутит, а его шутки переводить сложно. Поэтому знатокам английского строго рекомендую читать оригинал.
Константин Малков @travko
карма
7,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (113)

  • +9
    >Основная возможная проблема это потеря читаемости кода при большом числе операций производимых над коллекцией.

    Чушь, такая чушь.
    • –3
      А мне не нравится то, что убрали тип из выражения.
      list.getStream().filter(f -> f.getAge() > 21)
      

      Все это упрощает написание, но усложняет чтение. Да еще и нельзя узнать или быстро перейти в определяющий тип f. И хуже того, если поменяется тип списка при рефакторинге, а имена методов совпадут, то возникнет неприятный эффект коллизии.
      • +6
        Объявление типа — лишние символы в коде. IDE поможет определить тип.
        • +7
          Программы пишутся не для того, чтобы их понимали компьютеры, а для того, чтобы их понимали люди.
          • +6
            Все же не люди, а программисты. Ничто не мешает в конкретной команде/организации иметь style guide, запрещающий опускать тип аргумента. А так, вообще, тип если что поможет быстро определить IDE, ну и переменные/аргументы надо называть так, чтобы возникало как можно меньше вопросов, вот и все.
          • +2
            Если смотреть для чего пишутся программы, то программы пишутся для того, чтобы их понимали люди при помощи IDE;-) И у языков со статической типизацией типа Java здесь есть большое преимущество;-)
            • 0
              Кстати, это относится и к умолчательной реализации интерфейсных методов.
          • +1
            Вы знаете, уже сколько лет пишу на C#, где такие штуки уже давно существуют, никаких проблем нет. Это миф, что теряется читаемость.
      • +6
        Ничто не мешает написать
        list.getStream().filter((Person f) -> f.getAge() > 21)
        
      • +8
        В том же C# уже давно есть LINQ с лямбдами и выводом типов. По моему опыту, код работы с коллекциями в таком стиле проще пишется, лучше читается, и менее подвержен ошибкам. Это обкатаная технология, которая есть во большинстве мейнстрим-языков. Тут не о чем переживать, уверяю тебя.
      • +2
        А что если переписать так:
        persons.getStream().filter(p -> p.getAge() > 21);
        
        ?
      • +2
        Скажите, а в list.add(f), вас не смущает отсутствие явного указания типа элементов list? Если нет, то в чем именно разница?
        • 0
          Вы действительно не видите разницы в объявлении f?
          • +1
            Я не вижу разницы в том, является ли это объявлением, или нет. Где-то там есть и объявление list тоже, и в нем явно указан тип элементов. Зачем его постоянно повторять при действиях над этим объектом? И неужели при чтении кода неочевидно, что в list.filter(f -> ...) тип f — это тип элемента?
      • +2
        Это задача IDE. В .NET как-то с этим живут и не горюют.
        • +1
          Да помойму тут просто не понимание как это работает, и что такое f.
  • +16
    Не могу не процитировать:

    Java 8 has been influenced by Scala much more than how Scala has been influenced by Java (@mariofusco)
    • +3
      и правильно сделали
  • НЛО прилетело и опубликовало эту надпись здесь
  • +12
    Откуда столько ссылок на Scala? Все эти «нововведения» уже давно придуманы, и в большинстве своем даже не Мартином.

    Java является самой мощной промышленной платформой и развитие ее стандарта влияет практически на все крупные компании. Глупо ожидать тут гонки за «плюшками».

    Многие не понимают, что Java — это больше про JVM и платформу, а не про то, как ставить стрелочки — "->" или "=>". Лямбду хотели ввести еще в 7, но отказались — и правильно. На тот момент это был всего лишь синтаксический сахар на абстрактными классами.
    • +6
      Поддержу. При всей моей либви к Scala та же реализация лямбд в Java 8 — не выглядит как копия со Scala.

      Во-первых поддержка в со стороны jvm.
      А во-вторых сама идея Functional Interfaces хоть по началу и выглядит отталкивающе, но привыкнув понимаешь ее гениальность. Они не просто ввели лямбды, но сделали их совместимыми с огромным количеством уже существующих библиотек.
    • +1
      На самом деле Project Lambda начал с намного более амбициозного дизайна — например, там были полноценные function types и throws-параметры у дженериков (для строго типизированного прозрачного проброса исключений high-order functions). Но в процессе дизайна практически все плюшки выпилили.

      А в Java 7 лямбды не попали просто потому, что тогда дизайн был еще очень далек от завершения.
      • –1
        Но в процессе дизайна практически все плюшки выпилили.
        Может быть, в Java 10 добавят?;-)
        • 0
          Задумайтесь, почему их выкинули — может быть, отпадут лишние вопросы.
  • +7
    С одной стороны, если Java продолжит развиваться и реализовать все вокруг лябмда-выражений, как сделали в Scala, тогда возможно не будет надобности в Scala.


    Ну это как-то слишком категорично. Java 8 даже до C# не дотянет по удобству и лаконичности. А уж со Scala ей не тягаться. Лямбды — это хорошо, но не все. В верхних двух ответах к этому вопросу перечисленно множество других преимуществ Scala.

    Особенно интересно во что выльются макросы. На них уже весьма интересные вещи можно делсть.
  • +2
    В чем уверено большинство людей, так это в том, что они хотят запускать всякие штуки на JVM, но не хотят использовать для этого синтаксис Java.


    Что-то не уверен…
    • 0
      … насчет «большинства» :-)
      Большинство, как правило, об этом просто не задумываются.
  • +7
    Так понял, что новые Stream'ы — это эдакий LINQ с путанным названием. Поправьте, если неправ.
    А вообще, читая статью, не покидало ощущение, что Java смотрит скорее в сторону C#, нежели Scala. Подразумеваю именно вектор развития, а не источник появления новых фич.
    • 0
      Стримы — это элементы ФП над последовательностями. Путанное название и реализация как раз у Microsoft, впрочем, как всегда.
      • +3
        Хм, странно. Всегда думал, что Stream подразумевает поток данных. :)
        • +4
          Ну да. В чем проблема, что странно?
          • 0
            Это я про потоки ввода/вывода.
            • +4
              Да, есть пересечение по названиям, возможна путаница, что поделаешь. А слово «stream» — стандартное для обозначения такого рода объектов в ФП.
              • +2
                Мне кажется «flow» подходит больше
                • +5
                  Ленивые потоки как идиома известны еще со времен LISP. И название им streams. И LISP — семейство гибридных языков, так что эту идиому нельзя назвать чисто функциональной.
                • +3
                  flow не подойдёт, потому что flow — это скорее процесс «течения», в отличие от stream, который как раз есть сущность этого самого течения.
                  Тут ещё надо вспомнить, что в русском языке потоки ещё могут означать потоки выполнения, те что threads…
                  Но, имхо, I/O streams и streams данных достаточно различаются, и по контексту всегда можно понять, что есть что. То же касается и потоков в русском языке.
                  • 0
                    В русском языке есть термин «поток данных» = «stream».
                    Так что название «streams» вполне корректное также с точки зрения перевода.
            • 0
              «поток данных» — это более общая абстракция, чем «поток ввода/вывода».
        • +1
          Классику надо знать.
      • +3
        Как то IEnumerable и IQueryable понятней при работе с наборами данных. Так же как и AsParallel() более читабелен.
        Несмотря на то, что Stream стандартен с точки зрения ФП, не означает что он уместен в основном синтаксисе Java.
        Пока что это нововведения выглядят как не очень удачная попытка скопировать LINQ.
        Смотрел на Java с точки зрения переезда с C#, очень ждал аналога LINQ, но такое «кусочничество» пока отталкивает :\
        • +4
          Честно говоря, я в LINQ и вообще C# разбираюсь как баран в апельсинах, и на мой незамыленный взгляд вы с exvel видите тут «след C#» просто потому, что пишете на нем. А я тут вижу просто базовые функциональные примитивы работы с последовательностями, что отнюдь не rocket science, но раньше не было и этого. Сказать, что тут копируют C#, можно на абсолютно тех же основаниях, что и JavaScript, Scala, Python, Ruby, Haskell, или любой другой современный язык с элементами ФП.
          • +1
            LINQ немного больше чем просто кусочек ФП ;)
            Я год проработал Ruby разработчиком и там мне очень не хватало LINQ…
            При работе с коллекциями в самом деле очень удобно написать personList.Where(p=>p.Age>31) вместо list.getStream().filter((Person f) -> f.getAge() > 21). Хотите параллельно — AsParallel() и тд. Но вектор развития тяготеет к C#/.Net. Тот же аналог using блока — «try-with-resources» в 1.7. Может в 1.9 async будет ;)

            Просто не покидает ощущения, что нет четкого вектора развития, что печально. ИМХО.

            • +1
              Слабо знаком с c#, что есть в LINQ чего нет в руби? Пример выше в руби можно записать как person_list.select{|p| p.age > 31 }. В чем плюс LINQ?
              • +1
                В LINQ потом из этого (если операции проводятся над IQueryable) будет построено ExpressionTree, которое может быть преобразовано, к примеру, в SQL.
                • 0
                  Вы пробовали Sequel?
                  • +1
                    Sequel — только для бд. Это лишь частный случай применения.
                    А вот ExpressionTree вы можете не только в SQL разложить. Плюс их можно налету модифицировать.
                    В свое время мне понравилась идея добавления поведения объекту типа acts_as_paranoid и тд. На основе ExpressionTrees и декораторов сделал то же самое — IActsAsParanoid ;), но при этом, могу это разложить как на коллекцию объектов в памяти, так и на запрос в БД.
                    Вот тут интересная статья — community.bartdesmet.net/blogs/bart/archive/2009/08/10/expression-trees-take-two-introducing-system-linq-expressions-v4-0.aspx
            • 0
              каким это образом работа с массивами и бд в руби слабее LINQ?
              • +1
                Нет полноценных ExpressionTree. Я видел попытки повторить это но ощущения от этого были как от очень хрупкого карточного домика. Просто ИМХО.
                • 0
                  Если есть возможность получить исходный код метода или блока (а говорят в Ruby 1.9 она есть), то можно вполне элегантное решение сделать. Дерево будет строиться не в процессе компиляции (хотя Ruby и так не компилируемый язык), а в процессе исполнения.

                  Т.е. пишем метод, который из блока, переданного в метод, строит AST из исходного кода этого блока. Дальше достраиваем это дерево с помощью цепочек вызовов. Профит.
                  • 0
                    Можно сделать что угодно, но зачем? Даже в том же PHP есть подобное (http://habrahabr.ru/post/121976/).
                    Другое дело, что Microsoft конкретно так постарались, например, над тем же linq2sql, чтобы вообще избавить конечного пользователя от головной боли при проектировании связки база<->приложение и дать возможность удобно, быстро, качественно и не обращая на возможные узкости применения этой технологии (которой имхо вообще нет в случае с linq*) писать код. Пока ни один язык\платформа, с которой я сталкивался, не дает такой гибкости и легкости разработки, какую дает .NET.

                    Прошу не принимать меня за ".net-люба". Большую часть времени я работаю именно с Java и мне действительно рад тем ништякам, которая нам даст J8.
                    • 0
                      Я имею ввиду, что на Ruby и JS (TypeScript или CoffeeScript, чтоб было еще дотнетнее) можно сделать полноценный LINQ как в .NET, без всяких магических строк и с ExpressionTree.

                      Т.е. с базой можно будет работать как с обычной коллекцией в памяти, примерно так:

                      Ruby:
                      DB.Persons.select{|p| p.age > 31 }
                      


                      JS:
                      DB.Persons.select(function (p) { return p.age > 31; });
                      


                      TypeScript:
                      DB.Persons.select(p => p.age > 31 );
                      

                      • 0
                        LINQ не только для бд=) Очень удобно работать с коллекциями особенно, если надо провести сложную выборку с join и тд. Причем для сложных запросов можно пользоваться не MethodChain вызовами, а по сути просто написать запрос:

                        var result = from t in ints1
                        join x in ints2 on (t + 1) equals x
                        select t;
          • +1
            Выше поэтому и писал, что не копируют C#, а берут вектор развития. Стримы-Linq, лямбды, функ.интерфейсы-методы расширения. Это все направление движения, которое уже удачно обкатано в C#. Ведь языки, в отличие от других, сильно похожи. Понятное дело, что сами фичи изобретены не в Майкрософт.
          • +1
            >>Честно говоря, я в LINQ и вообще C# разбираюсь как баран в апельсинах, и на мой незамыленный взгляд вы с exvel видите тут «след C#» просто потому, что пишете на нем

            Точно. В шарпе то синтаксический сахар для монад есть, а в джаве насколько я вижу только лямбды осилили.

            • +1
              У меня лично твёрдое убеждение, что качество кода, написанного на любом мэйнстримовом языке, мало кореллирует с синтаксическим сахаром, доступным в этом языке.
              • +2
                Синтаксический сахар — это удобство и читаемость. Экономия времени при сопровождении.
                Когда есть сахар в языке, многие сложные конструкции можно сильно сократить.
                Называть ли код с сахаром качественнее аналогичного без оного? Я бы сказал, что зависит от приоритетов при разработке. Если нужна оптимизация, то сахар может даже помешать. Но в большинстве повседневных задач, скорее, влияет положительно.
                • 0
                  Я скорее имею в виду, что действительно хороший программист и без сахара найдёт способ написать понятно и корректно. А 90% кода что с сахаром, что без сахара, будет читаться и работать паршиво.
                  • 0
                    С этим сложно поспорить. :)
                  • +2
                    >>и без сахара найдёт способ написать понятно и корректно
                    понятно и корректно — но только в рамках заданного языка. без синтаксического сахара.
                    Ну вот хоть тут будет супер-пупер программист на асме, но даже середнячок на шарпе клиента к БД напишет понятнее.
                    И нормальный программер на OCaml напишет какой нить парсер в 100 раз понятнее, чем это сделает сишник.
                    • 0
                      У каждого языка есть ниша, в которой он выглядит лучше других :-) Иначе он становится мертвым.
    • 0
      Это далеко не LINQ — ничего не было интегрировано в язык, т.е. это чисто библиотечная функциональность.
      Суть стримов раскрывается в цитате из javadoc:
      A sequence of elements supporting sequential and parallel bulk operations. Streams support lazy transformative operations (transforming a stream to another stream) such as filter and map, and consuming operations, such as forEach, findFirst, and iterator. Once an operation has been performed on a stream, it is considered consumed and no longer usable for other operations.

      For sequential stream pipelines, all operations are performed respecting in the encounter order of the source, if the source has a defined encounter order. For parallel stream pipelines, unless otherwise specified, intermediate stream operations preserve the encounter order of the source, and terminal operations respect the encounter order of the source, if the source has an encounter order.
      • +1
        А что было интегрировано в C# для поддержки LINQ, помимо добавление расширяющих методов?
        • 0
          Как минимум замыкания и цитирование. Допустим, первое как-то поддержено в Java, но второго даже нет в планах.
          Еще можно посчитать доработку парсера под linq-запросы.
          • 0
            А замыкания к LINQ каким боком? Хотяяя… Чтобы не сыпать вопросами, не подскажите, где можно вразумительно почитать про реализацию?
            • 0
              Не совсем про реализацию, но читал в свое время «LINQ in Action». И MSDN, конечно.
          • +1
            А что такое цитирование в C#? Я бы сказал, что основные доработки в C# выразились в доработках компилятора (парсинг, расширение использования утиной типизации, пр.).
            • 0
              Квацитирование — преобразование выражения в ExpressionTree. Попробуйте без этой плюшки сгенерировать, например, SQL-запрос из from p in persons where p.Name == "John" select p;
              • 0
                Ок, понятно, иными словами создание AST из linq и сохранение структуры данных. Еще, из конкретики, extension methods добавили. У них, конечно, область применения широка, но они явились способом поддержки linq в базовой библиотеке.
      • +2
        Та часть LINQ, которая про работу с коллекциями — она точно такая же библиотечная функциональность. По сути в Java вместо extension methods приделали functional interfaces. Остальное — лямбды, замыкания и прочее — такое же. А Stream, как я понимаю — это тот же IEnumerable. Но в целом LINQ с его expression trees и специальным синтаксисом помощнее, да.
  • +5
    Странное заключение.

    Стримы — как раз больше про удобство, чем про магическое ускорение существующего кода. Для 99.99% ситуаций модель распараллеливания стримов с разворачиванием fork-join пула — явный overkill. Чересчур универсальное решение, чтобы быть быстрым. Если кому-то при обработке каких-то коллекций действительно нужна скорость и мощь всех имеющихся ядер — уже написанный руками код почти наверняка работает быстрее, чем будут работать стримы.

    Преимущество стримов — сокращение, читабельность и надежность не критичного по производительности кода.

    То, что вызывает опасения, так это функциональные интерфейсы. При не правильном использовании может вызывать кучу головной боли.
    ???

    Про HashMap можно было упомянуть новые удобные функции. Атака хеш-таблиц с корзинами для разрешения коллизий — это конечно мега распространенная ситуация…
    • 0
      Для 99.99% ситуаций модель распараллеливания стримов с разворачиванием fork-join пула — явный overkill.

      Новый ForkJoinPool теперь использует work-stealing планировщик и рандомизованные очереди, что по некоторым замерам увеличило производительность более чем в 10 раз.
  • 0
    (промахнулся)
    • +2
      Extension Methods — это не хилая такая фича языка. Подобное есть во многих языках, но в Java нет и не обещают.

      А по поводу что еще…

      var l2 =
          (
              for e1 in list1
              join e2 in list2 on new {e1.Field1, e1.Field2} equals new {e2.Field1, e2.Field2}
              where e1.IntField > 0
              select e2.Field3
          ).ToList()
      


      И если с необходимостью SQL-подобного синтаксиса еще можно поспорить (джойнить без него — пытка), то без анонимных типов джойнить и группировать печально. На каждый чих по отдельному типу — это, мягко говоря, не удобно.

  • +1
    Кстати, тут вот еще хороший синопсис нововведений в Java 8: www.techempower.com/blog/2013/03/26/everything-about-java-8/
  • +3
    Интересно, почему в статье назвали интерфейсы с default-методами функциональными? Функциональный интерфейс — это просто интерфейс, у которого есть один абстрактный метод, вот и всё. default-методы к функциональности интерфейса непосредственного отношения не имеют.
    • +1
      О! Первый возглас в числе комментов.
      Хотите ответ — а назвали так, потому, что автор оригинальной стать не шарит! Да и переводчик тоже, иначе исправил бы.
      • 0
        Да и авторы фичи, которые предлагают ставить аннотацию @FunctionalInterface на интерфейсах с дефолт-методами тоже.
  • –2
    Жаль, что Java так инертна в развитии — думал перейти с C# на Java но даже 8ка пока не мотивирует :-(
    • +1
      Перейдите на другой JVM-язык, тут вам и JRuby, и Scala, Clojure, Groovy. Любой из этих языков получает доступ ко всему codebase java. В проектах эти языки можно совмещать с Java, как душе угодно.

      Я, например, перешел на Scala, не нарадуюсь.
    • 0
      Нет смысла менять шило на мыло. Если вам нужна JVM, смотрите в сторону Scala или Clojure. А джаву на уровне библиотек освоите и хватит.
    • 0
      Ну вообще-то люди не просто так переходят с одной платформы на другую.
      И если для вас самое главное это синтаксический сахар, то скорее всего оно вам абсолютно не нужно.

      Не для холивара.
      Я в какой-то момент окунулся в платформу NET. Поработал с VS и прочим рабочим окружением разработчика.
      И понял, что никакие синтаксические плюшки C# не перевесят весь этот глобальный ужас рабочего окружения связанного с NET.
      • +2
        А что не так с окружением?
        • +2
          Только не надо на мои слова бросаться как на амбразуру)
          Это всего лишь впечатления от работы с NET стеком.
          Просто после Java, все что связано с NET вызывало удивление.

          Ну во первых, это IDE. Этот монстр, даже в экспресс версии, устанавливается на несколько гигабайт.
          После IDEA, я просто тихо офигевал от мелочей о которых раньше просто не задумывался.
          Простейший пример, банальнейший, в java мире, все IDE что я видел, по умолчанию сохраняют вывод консоли в отдельном окне и даже если программа вылетела, можно полистать и посмотреть что там происходило.
          VS же отлично закрывает свою консоль и в результате нихрена непонятно где искать вывод консоли после вылета приложения. Подчеркну для тех кто на бронетехнике, я говорю про поведение по умолчанию. Понятно что всегда можно что-то сделать. Даже написать свою IDE. Но таких вот мелочей слишком уж много в процессе выяснялось.
          Потом вся виндовая инфраструктура VCS, интернет серверы и прочее. Проблемы с версиями NET и т.д.
          Опять же windows only продукты. Чтобы писать под NET, только винда, серверы только винда и т.д. со всеми вытекающими. Да, для кого-то это наоборот жирный плюс, например для продажников ;)
          Опять повторюсь для тех кто на бронетехнике, это не означает, что нет mono, но речь сейчас не о нем.
          Ну и просмотр цен на все это непотребство завершает картинку.

          В общем ощущения были, что мне загипсовали руки и заставили вот так работать))
          Опять же повторюсь для тех кто еще не слез с бронетехники. Все мои слова не означают, что на NET стеке ничего невозможно разработать. Это всего лишь общие впечатления после небольшого погружения в мир NET.

          P.S. Ну а после ознакомления со Scala, вообще стало непонятно, зачем нужен C# ))
          • +1
            Оно, видимо, не рассчитано на ваш сценарий использования.

            Там, где я работаю, стек технологий MS используется весьма удачно: в VS очень удобно объединяется работа с кодом, системой контроля версий, таск-трекером, работой с билд-сервером. В общем со всем, что требуется для работы. Это требуется настраивать (1 раз), это не подходит для индивидуального разработчика, но не на таких и ориентировано.

            Студия с решарпером — отличная IDE для команды разработчиков, если работать так, как предполагали в MS, создавая студию.

            А про скалу… на ней пока мало кто пишет. Жаль, конечно, но что поделать.
            Да и поддержка IDE не идеальна. Несомненно, что для Scala IDE не столь необходима, как для Java. И в JetBrains проделали великолепную работу для поддержки Scala. Но будим честны: IDEA не понимает сложный scala-код. Как только появляется scalaz или path-dependent types идее плохеет.

            Да и в плане GUI Java-стек в роли догоняющих. Я надеюсь на JavaFX (и ScalaFX), но это пока новая, не обкатанная технология.
            • 0
              Да, вы все верно поняли.
              Оно именно для моих целей не удобно.
              Да Решарпер отличное дополнение. Но вот… купить студию за ~1000$ + еще решарпер ~200$…

              А на скале уже и яндекс щас пишет свои сервисы.
              Так что времена «пока мало кто пишет» прошли.

              Если подвести итог того что я видел, на Scala люди пишут свои продукты.
              Т.е. когда люди заинтересованы в собственной эффективности.
              Для заказчиков, т.е. оутсорс, продают стандартные известные вещи, которые проще и выгоднее продать.
              • 0
                Но вот… купить студию за ~1000$ + еще решарпер ~200$…

                А что поделать? Считайте, что IDE стоит 1200 =)

                Так что времена «пока мало кто пишет» прошли.

                Я говорю с точки зрения наемного программиста: в Питере мало кто пишет.
          • +1
            Ясно. Ну, видимо, дело привычки. Я, к примеру, давно привык к громоздкости окружения. Все равно ставится один раз.
            Про проблемы с версиями не очень понял. Точнее сам не сталкивался за два года.
            Насчет консоли, есть окно «Вывод». Оно доступно как во время дебага, так и редактирования кода. Туда скидывается вся инфа по грузящимся сборкам, исключениям, отладочным записям Debug.WriteLine() и основной консоли Console.WriteLine(). Окно очищается только перед запуском отладки, а все записи сохраняются после.
      • +2
        >ужас рабочего окружения связанного с NET.

        А мужики-то не знали.
        • 0
          Есть такая фраза «Лучшее враг хорошего».
          Понять разницу можно только поработав и с тем и с другим.
          • +2
            Поработал и с тем и с другим, и могу с увереностью сказать, что у вас Синдром утенка.

            Т.к. у меня совершенно противоположено представление, так как начил с .net.(могу с увереностью сказать, что и у меня тот же самый синдром).

            P.S. Насчет IDE это вообще не показатель. т.к. по сравнению со всеми IDE в VS меньше всего багов.(покрайней мере раздражающих)
            P.P.S. Консоль по умолчанию не закрывается в VS. Всегото нужно на другую вкладочку переключится.
            • 0
              Про утенка вы мимо))
              Если коротко, я начинал с dephi, продолжал C# и сейчас вот Java/Scala.
              Да, после нескольких лет Java возникла необходимость опять на NET некоторые вещи сделать.
              Отсюда и впечатления.
              • 0
                Мне не нравится Java тем, что решить задачу скорей всего можно не множеством с пособов, а ограничеым числом(совсем не элегантных).

                Пример с перегрузкой оператовров. Допустим сумма векрторов и тп.
                • +1
                  Ну вы сравнили.
                  Java это же матерый ынтырпрайз)) там элегантность никого особо не интересует.
                  Для красоты и элегантности та же Scala используется… или другие JVM языки.
                  • +1
                    Дак, вот в этом то и беда у каждой предметной области есть свои бизнес-правила. и их приходится реализовывать не так красивои и интуитивно понятно.
                • 0
                  IMHO, перегрузка операторов без статической типизации — самоубийство :-)
                  • 0
                    Хотя при поддержке IDE для языков со статической типизацией (а-ля Java) могли бы и сделать
          • +1
            Я работал с IDEA, VS10/12, Xamarin studio (monotouch). Не вижу каких-то принципиальных жестей ни в одной из этих сред (если уж мы про среды).
  • 0
    Лямбда это конечно сахар, жаль ExpressionTrees нету :(
    Вот где прорыв бы был.
  • 0
    чего то я не нашел, а implicitly typed будут введены в java8?
    • +1
      `var` из C#? Нет. Это было бы слишком хорошо, видимо.
      • 0
        ну не только из C#, я насколько понимаю в scala такое тоже есть
        • +1
          Я всего-лишь привел наиболее известную аналогию. Не сомневаюсь, что есть и другие примеры.

          И в Scala не такое. Там из-за другого синтаксиса `val` и `var` являются обязательными элементами, а не заменяют имя типа, в отличии от C# и того, что бы могло бы быть в Java.

          C#:

          int i = 1; // vs.
          var i = 1;
          


          Scala:

          val i: Int = 1 // vs.
          val i = 1
          


          И я не уверен можно ли назвать аналогией `auto` из C++: там, кажется, есть отличия.
          • 0
            Спасибо за разеснение, я что в Scala не силён, что в С++.
          • 0
            Ой, а можете реальный юзкейс из мира джавы привести, где это было бы полезно?
            Я правда не знаю, очень интересно.
            • +2
              Вот вам реальный кейс из C# (в Java аналогично):
              var elements=collection.Where(x=>x.foo>bar).Select(x=>x.baz);
              foreach(var element in elements)
              {
                  Dosmth(element);
              }
              

              Не нужно думать, что за тип у element, компилятор и среда сами его выводят. Т.е. и читать просто, нет загромождения, и в то же время невозможно ошибиться с типом, так как var это все та же статическая типизация. В этом примере, кстати, даже foreach можно в монаду запихнуть, просто не всегда бывает удобно.
            • +2
              Выше уже дали пример кода.

              Мне же более наглядным кажется пример вроде такого (C#):

              Dictionary<TreeNode<BusinessObject>, IList<TreeNode<BusinessObject>>> childrenByParent = ... а что я вообще собирался сделать? забыл =(
              


              Все имена вымышлены, все совпадения случайны

              По моему опыту при правильном именовании тип и так понятен из контекста. И захламлять код декларациями, выглядящими как песня с припевом совершенно излишни.

              Или взять Scala. При использовании Scalaz вот такой тип является вполне обычной практикой: ValidationNel[String, List[Category]]. Это если забыть о том, что на самом деле это Validation[NotEmptyList[String], List[Category]]. Кстати, пример из моего кода. И это еще упрощение, на практике вполне возможно будет не String. Подобные типы иногда появляются на две строчки кода и больше ни где не используются — зачем их писать? Там и так все понятно: если в коде используется Validation, то он используется постоянно и не надо об этом каждый раз напоминать.
          • 0
            В общем случае в C++ так же,
            auto i = 1;
            // or, more like C++11
            auto i {1};
            
            • 0
              Я не разбираюсь в C++11, но помню как меня удивило использование autoв лямбдах. auto тип параметра чтоли… не помню.
            • 0
              В общем случае в C++ так же,
              auto i = 1;
              // or, more like C++11
              auto i {1};
              



              Знаю что это пост 13-го года… Но на случай если кто-то сюда случайно попадет так же как и я.
              Ваши 2 выражения не идентичны. В первом случае auto выведется в тип int — все правильно. А во втором в…
              std::initializer_list<int>
              

              Такая особенность выведение типов у auto.
  • +2
    Как мне нравится последнее время такая тенденция. Берется статья и переводится.
    Блин, и тут даже не выйдет объяснить автору, что он балбес. Ибо в любом случае стрелки на автора оригинала, и спорить нужно там.
    А тут перевод. Перевод статьи, автор которой совсем не разбирается в теме.
    • +1
      Можно пойти по ссылке на статью-оригинал и отписаться там :)
      • 0
        Ага, и при этом теряется русскоязычная часть аудитории, ибо 90% людей не пойдут за оригиналом, предпочитая отписываться на месте. Еще бы комментарии автоматически постились в оригинальный блог… :-D Но чудес не бывает, до «Web 4.0» мы еще не дожили.

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