Pull to refresh

Comments 103

лучше бы структуры и unsigned добавили как в шарпе, ей богу. Без лямбд можо прожить
Ну учитывая то, сколько они делали поддержку строк в switch, структуры тоже будут очень не скоро.
UFO just landed and posted this here
Чтобы сборщик мусора отдыхал :)
UFO just landed and posted this here
Структуры располагаются в стеке потока, а не в куче, в следствии чего не требуется освобождение памяти сборщиком мусора.
В Java есть примитивы(byte, short, int, float,...) это аналоги структур в .NET (они располагаются в стеке). Однако нет возможности создать пользовательский тип который бы располагался в стеке.
UFO just landed and posted this here
В C# от такой возможности больше пользы, чем проблем. Там структуры — это очень редко используемая штука, но иногда они могут очень сильно поднять производительность.
UFO just landed and posted this here
По не воле вспомнилось высказывание: «Если бы в Java действительно работала сборка мусора, большинство программ бы удаляли сами себя при первом же запуске».
Еще массивы структур располагаются в памяти одним блоком, а не массивом ссылок на объекты в куче. В некоторых случаях это принципиально.
UFO just landed and posted this here
UFO just landed and posted this here
Если кратко, в стеке создаётся не сам объект, а только его поля по списку.
Например, при парсинге JSON'a: удобнее сделать структуру. Сейчас приходится создавать «структуру», в которой много вложенных классов с геттерами/сеттерами.
UFO just landed and posted this here
Публичные поля — это не очень классно.
Если брать задачу парсинга JSON'a: то там сеттеры вообще не нужны.

UFO just landed and posted this here
А чем принципиально отличаются публичные поля в структурах (С#, C++) от публичных полей в объектах?
Вам заголовки файлов или TCP пакетов читать приходилось? Со структурами это делать куда приятнее, чем вручную парсить byte[].
UFO just landed and posted this here
сам фиче-реквест был создан 16 лет назад. Тогда Sun сказали: «Никому из наших клиентов (кто платит за тех.суппорт) это не надо»
и еще честные дженерики, а не эту фигню с боксингом
И protected нормальный! чтобы всем детям видно было, а не только тем что рядом лежат
UFO just landed and posted this here
Про честные дженерики(если я правильно понял автора и не забыл джаву), имеется в виду, что в джаве нельзя написать:
ArrayList<int> 

Можно только:
ArrayList<Integer> 

Что может привести к лишнему боксингу/анбоксингу:
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(42); // Тут произойдет автоматический боксинг
int number = list.get(0); // а тут анбоксинг


В С# можно написать:
List<int> list = new List<int>();
list.Add(42); // никакого боксинга
int number = list[0]; // никакого анбоксинга
Вы понимаете, что такие генеральные переделки сломают совместимость со старыми JVM?
Вообще не совсем понятно зачем держаться за нее? В мире .Net никого не удивляет, что новая программа может попросить новую версию фреймворка.
И именно по этому многие выбирают java для серверов :)
В смысле — новая программа требует новый фреймворк, новый фреймворк требует новую ОС, ОС требует новое железо?
Ну да, такой шанс есть. Но на данный момент мне почему-то кажется, что Oracle уже могли бы пожертвовать обратной совместимостью и избавиться от кучи костылей. Тем более, что насколько мне известно, совместимость все равно оставляет желать лучшего и не является стопроцентной. Поправьте меня, если я ошибаюсь.
Совместимость в джаве очень сильная. Это одна из главных стратегических линий платформы.

Преимуществ уйма.
Вы можете без опасений пользоватся старыми либами и они будут работать так как надо.
Например ваш проект использует либу А, которая давно не поддерживается и не совместима с новым фреймворком и вы захотели добавить либу Б, которой нужна новая версия фреймворка.Что выбрать?
И еще. Серьезное банковское серверное приложение. Вы обновили фреймворк, потеряв совместимость. Вы должны молить бога чтоб баги всплыли в тестах, а не на продакшене.

Разве в С# нет обратной совместимости?
Я видимо неточно написал. Конечно старое приложение и старая либа под новым фреймворком заработает. Обратное неверно — новое приложение под старым фреймворком не заработает.
Я не предлагаю так сделать в Java. Я попытался пояснить, что, вероятно, имелось в виду под «честными дженериками» в данном случае.
Но тем не менее? я хочу заметить, что и в Java и C# дженерики появились уже после релиза языка. Так что проблемы совметимости были не только у авторов Java.
в итоге MS сделали довольно криво решение — System.Collections.Generic namespace
Дело не только в том, как писать. В Java все эти дженерики при компиляции преобразуются в инстанциированные классы (что-то вроде ArrayListOfInteger). В рантайме инстанциировать новый дженерик нельзя, только путем компиляции в рантайме. И в reflection не очень удобно анализировать дженерики.

В .NET дженерики «честные» (после компиляции так и остаются дженериками), можно инстанциировать в рантайме и reflection гораздо приятнее.
Если бы в рантайме оно преобразовывалось в ArrayListOfIntegers — это не было бы проблемой как раз, мы бы по прежнему могли понять, что происходит. Проблема как раз в том, что оно преобразуется просто в ArrayList. Все, что в угловых скобках, по сути удаляется после компиляции
Да, точно, спасибо за уточнение. Давно изучал этот вопрос, подзабыл детали.
Еще, насколько я помню, такая особенность позволяет в рантайме подсовывать элементы «не того типа» в ArrayList, т.к. у него тип элементов Object. Но это только при намеренном желании сломать, конечно.
UFO just landed and posted this here
Вообще-то, в C# так можно не из-за «нормальных дженериков», а из-за того, что int — это класс, а не примитив
Вообще int это структура.
UFO just landed and posted this here
UFO just landed and posted this here
Такими темпами Java безнадежно отстанет и загнется (платформа JVM никуда не денется, но язык вполне может смениться).
Пока в Java будут реализовывать поддержку фич десятилетней давности, другие языки будут обогащаться новыми фичами.
Покуда они будут компилиться в код, выполняемый JVM и без гемороя переезжать с платформы на платформу — нет проблем.
Async/await? Уже есть: commons.apache.org/sandbox/commons-javaflow/
Я к этому делу прикрутил ещё и kryo и пишу бинарный образ continuation'а в blob в БД. Можно хоть перезагрузить сервак, пока поток ждёт. Я это использую для того, чтобы программировать workflow на java без всяческих event'ов и listener'ов. Получается прямо как BPEL, только Java вместо XML. А continuation'ы использую, чтобы ожидать ответ от пользователей. Пока пользователь не выполнит назначенную ему задачу, continuation будет мирно лежать себе в БД. Единственный минус javaflow — плохо поддерживается reflection и встроенный механизм proxy. Последний мне был нужен, поэтому я взял в руки ASM и запилил свой Proxy с поддержкой javaflow.

Dynamic Runtime? Я правильно понимаю, что это просто библиотека над стандартным CLR + ключевое слово dynamic? Последнее не знаю, зачем нужно, а библиотеки в Java, думаю, и так подобные имеются. Плюс появившаяся в Java 7 инструкция INVOKEDYNAMIC.

Extension methods не нужны (и порой даже вредны), без LinQ, конечно, тяжело, но есть Querydsl. С появлением лямбд всё будет ещё веселее. Наверняка кто-нибудь сделает querydsl на стероидах. Теоретически, если сюда подключить манипуляцию с байт-кодом, то можно получить вариант, практически не отличимый от LinQ. А вот то, что не сделали нормальный вывод типов — это жаль, конечно.

Зато в .NET нет java.util.concurrent (или уже появился?), нет перекомпиляции методов JIT'ом в более быстрые аналоги на основе статистики выполнения (или тоже уже сделали?) и дальше по списку.
Лямбды всё равно будут кастрированными и с граблями.
Почему то все забывают, что ламбды то нам обещают в 8-ка, а вот полноценных замыканий нет. Сейчас изнутри лямбды (как и безымянного вложенного класса) можно обращаться только к финальным локальным переменным метода в котором они создаются. Как это не поразительно в 8-ке на собираются менять этот ужас. Даже в JavaScript замыкания реализованы, а в Java нет.
Во-первых, там вводится понятие «эффективно финальных переменных», т.е. которые хоть и не помечены final, но присваиваются только один раз, их можно сразу использовать в лямбдах. Во-вторых, возможность модификации любой переменной — это опасно в связи с многопоточностью, об это сразу будут обжигаться много народу. В-третьих, полноценные замыкания это опять генеральная переделка JVM, т.к. придётся вводить какую-то сущность типа «указатель на фрейм стека» или что-то подобное.
Замыкания и лямбды можно реализовать на уровне синтаксиса без добавления новых инструкций в байткод и изменения виртуальной машины. Доказательство: C#, Scala.
Можно, только это будет либо
1) неэффективно с точки зрения JVM (плохая HotSpot-компиляция, много временных объектов, туча мелких анонимных классов)
2) это будет другой язык, не только с другим синтаксисом, но и с другой семантикой
Ну вот и пользуйтесь тогда Scala. (только не говорите, что там проблем нет)
Полноценные замыкания элементарно имитируются через ObjectHolder
Кто бы сомневался. А ведь первоначально вообще планировалось выпустить Лямбду в составе Java 7 ещё несколько лет назад.
Всё, точно пора мигрировать на Groovy или Scala. Они намного динамичнее развиваются.
Жаль только, что у скалы компилятор тормозной, хреновая поддержка IDE, проблемы с производительностью и ещё куча мелких проблем. А груви так вообще какая-то полудинамика.
Марк задаёт вопрос: а какие вообще были варианты у ребят из Oracle?

Варианты больше напоминают отмазки. Почему-то новые версии .NET вместе C# выходят регулярно, синтаксических плюшек там гораздо больше.
Видимо просто у Oracle другие приоритеты. Или большому Oracle просто не до Java.
Я думаю стоит учесть тот факт что .NET идет под Windows платформу только, MONO не рассматриваем. Исходя из этого времени на тестирование .NET релиза нужно заметно меньше, в отличие от Java с таким набором поддерживаемых платформ.
Почему не рассматриваем? Рассмотрите. Он уже давно стабильный и полностью поддерживает спецификацию.
UFO just landed and posted this here
А вот, кстати, любопытно, где больше разработчиков — в подразделении Microsoft по C# + разработчики mono или в Oracle в подразделении по Java? Не думаю, что разница в разработчиках такая же огромная, как в функциональности.
Да, не спорю. И Java более старая платформа, поэтому многие новые фичи делаются через костыли ради обратной совместимости.
Но это все больше касается JVM. А синтаксис можно делать сколь угодно разнообразным и красивым, если его можно скомпилировать все в тот же байткод. В этой части затраты одинаковы.

Лямбда-выражения появились в C# 3.0, 6 лет назад. В Java они будут добавлены только в следующем году. Реально 7 лет требовалось, чтобы реализовать лямбда-выражения? Думаю причина тут все-таки не в трудоемкости, а в приоритетах.
Lambdas это не просто синтаксис, они компилируются в байт-код с использованием таких фич как method handles и invokedynamic, которые появились только в Java 7.
Я в целом про то, что синтаксис Java не улучшается годами. То, что в других языках есть из коробки, здесь реализуется через паттерны.
Я в целом про то, что синтаксис Java не улучшается годами.

А нужно оно? Золотое правило: работает — не трожь.

То, что в других языках есть из коробки, здесь реализуется через паттерны.

То что можно сделать библиотеками — не нужно делать на уровне языка. Однородность и простота синтаксиса это плюс а не минус.
То, как их сделать — вопрос реализации. Можно было реализовать по-другому, раньше и с замыканиями.
Можно было, для каждой лямбды генерировать анонимный класс (тем самым дополнительно напрягая GC).
ничего подобного, лямбды это синтаксический сахар над интерфейсами с одним методом.
Нет. Прочитайте Translation of Lambda Expressions. Компиляция лямбды создаёт в базовом классе скрытый метод наподобие static void lambda$1(...) { }, а при выполнении при помощи некторой «магии» берётся ссылка на этот метод и превращается в экземпляр интерфейса (нечто вроде dynamic proxy).
Вообще, я считаю что зря они уходят в функциональное программирование с этими лямбдами (поправьте, если не так).
На данный момент я нашел только одно применение лямбдам: эмуляция C# делегатов.
Код читаться будет проще во многих случаях. Вместо:

void DoSomething()
{
    Console.Write("Hello ");

    Task.Factory.StartNew(PrintWorldWithDelay);

    // тут много кода

    Console.ReadLine();
}

// и тут много

void PrintWorldWithDelay()
{
    Thread.Sleep(2000);
    Console.Write("World! (after 2 sec)");
}

так:

void DoSomething()
{
    Console.Write("Hello ");

    Task.Factory.StartNew(() =>
    {
        Thread.Sleep(2000);
        Console.Write("World! (after 2 sec)");
    });

    // много кода

    Console.ReadLine();
}


Хотя, кому-то такой стиль может показать «неправильным», но я без него жить не могу. Намного быстрее понять, что происходит и в какой последовательности.
new Thread(new Runnable() { public void run() {
        Thread.sleep(2000);
        System.out.println("World! (after 2 sec)");
    }}).start();


не катит?
Или даже так:
new Thread(
      public void run() {
        Thread.sleep(2000);
        System.out.println("World! (after 2 sec)");
    }).start();
Катит, конечно, но многие ругают Java именно за многословность.
В случае с лямбдами public void run() превратится в () =>
экономия не очень существенная. И совсем другое дело, если бы принципиально это было невозможно и пришлось бы всегда писать отдельные методы.
Хотя наверняка найдутся и более удачные примеры. А вот лично мне как раз больше мешает боксинг дженериков, как писали выше.
Если точнее, то
new Runnable() { public void run() {
плюс ещё одна закрывающая скобка. И по многим правилам оформления обычно это разбивают на минимум 2 строки.
посчитайте, вроде обычных и фигурных скобок правильное количество.
на строки я не разбивал, чтобы было похоже на указанный выше C# пример, но это все не принципиально, мы ж говорим об общем принципе.
Проблема в том, что будет создан анонимный класс. Это довольно дорого (если это делать очень часто). Лямбды — это не только новый синтаксис, это очень полезная вещь. Можно было бы, конечно, просто оптимизировать создание анонимного класса из интерфейса, в котором определен только один метод, но это было бы непрозрачно — в вашем примере вариант с Runnable был бы оптимизирован, а вариант с Thread — нет
Из очевидного — работа с коллекциями. А вообще это еще одна степень свободы, которую можно использовать при проектировании.
Лямбды позволяют реализовывать красивую работу с коллекциями (см. LINQ) а в последствии и с событиями (см. Rx).
Для баланса комментариев напишу, что лично мне лямбд больше всего в современной Яве не хватает. И, скажу более, пожалуй единственное, что ей не действительно не хватает и что реально важно.
Сейчас вполне реально и возможно даже нужно писать multi-language проекты. Например Java/Scala. Все равно все собирается в байт-код. В Scala вам и лямбды будут)
А ничего что из лямбды нельзя поменять локальную переменную?
Через трюк — можно.
int[] result = new int[1];
callSomething(() => { ...; result[0] = x });
А без трюков — будет много сложностей, как у людей, так и у компилятора и JVM, поверьте.
Это не трюк, а костыль. Когда я в первый раз увидел подобный код (там использовался AtomicInteger), я включил повышенный внутренний уровень опасности, ожидая хитрой работы с потоками, оказалось зря — это был как раз этот «трюк»: использование вещей для этого не предназначенных, чтобы обойти искуственное ограничение. По-моему, сложность для людей очевидна.
Вот допустим, метод callSomething запускает лямбду асинхронно в новом треде, и после этого вызывающий метод сразу завершается. Лямбда посчитала результат — какую она локальную переменную сможет теперь изменить, если уже от вызывающего метода и стека не осталось? Ну вы-то сообразите, может быть, что такого делать нельзя, а как это компилятору объяснить — сможете?
Вы не тот вопрос задаете.

Если java-программист хочет использовать замыкание (изменение локальной переменной из лямбды), то java сообщество не бьёт его палкой, а рекомендует использовать костыль в виде int[] или AtomicInteger. Возникает вопрос, почему это окостылевание не делает компилятор, если оно и так общепринято? Ответ я не знаю.
Если java-программист так хочет сделать — почти наверняка он пишет не очень хороший код. И поэтому то его и надо бить палкой.
Это, кстати, Thread Unsafe код и, соответственно, гонка, даже если callSomething корректно передаст вашу Лямбду в другой поток.
Как заранее понять, что он thread unsafe (не заглядывая внутрь)? (и как дать это понять компилятору?)
Компилятору абсолютно пофигу сейф ваш код или нет. Дать ему понять, с-но никак.
Заранее понять очень просто, если можно доказать, что всегда будет ребро happens-before, значить трейд сейф, иначе не трейд сейф. Тут на момент вызова лямбды не будет таких ребер. Можно переписать например так:
ArrayBlockingQueue result = new ArrayBlockingQueue(1);
callsomething(() => {… result.put(x);});
Я, если честно, вообще полагаю, что зря в Яве локальные переменные не являются final по умолчанию. Так что ничего страшного, их вообще крайне редко приходится менять.
В Intellij IDEA можно поставить декларирование локальных переменных всегда финальными. Советую.
Единственное когда это мешает, когда переменная кидает не RuntimeException, при какой то инициализации. Скажем мы хотим вытащить значение какого то обьекта из чего то, а эта операция вызывает эксепшн, с которым ничего вовсе не хочется делать. И выходит что то типо
...
Foo foo = null;
try{
    foo = getFromSomewhere();
} catch (IDontCareException e) {
    log.info(e, "looks like foo is not there);
}

if (foo != null) {
    doSomethingWithFoo(foo);
}

doSomethingElse();
...
А почему не так?
...
try{
    final Foo foo = getFromSomewhere();
    doSomethingWithFoo(foo);
} catch (IDontCareException e) {
    log.info(e, "looks like foo is not there);
}

doSomethingElse();
...

И правильно. В иммутабельном стиле программирования потенциально меньше ошибок
ничего. ибо не нужно. ибо за такой код надо по рукам бить
Sign up to leave a comment.

Articles