Comments 147
Итераторы доступны со 2 версии.
Классные нововведения, особенно с Collections и лямбдами. Вот только про интерфейсы — как-то неясно, теперь они не такие интерфейсные, чтоль =)
Ну, в них по прежнему нету полей (только static final). Больше всего похоже на traits или mixins из других языков.
Мне скорее ломает мозг не само введение трейтов — они успешно применяются во многих языках, а то, что не было введено специальное ключевое слово trait. В этом случае default было бы не нужно. Все-таки интерфейс с реализациями — это странно и противоречит многим книжкам по ООП.
Думаю, что введение нового ключевого слова — это довольно неприятная штука. Поэтому и сделали
С другой стороны
for(:)
вместо for(in)
или foreach
, @interface
вместо annotation
и т.д.С другой стороны
enum
ввели в свое время.Добро пожаловать в language design для устоявшихся языков, где для обеспечения обратной совместимости дизайнерам приходится лезть из кожи вон.
Ну если вспомнить, для чего вообще ввели в жаве эти дефолты, то все станет понятно. А ввели для того, чтоб расширить базовые интерфейсы коллекций лямбда-примочками, не поломав уже написанный код, явно или неявно использующий оные интерфейсы.
Странно возможно, потому что вы java программист и привыкли к такой модели, ясное дело, что в книгах связанных с java и ООП будет перечить, но ООП не такая уж и стандартизированая штука, например взять тот же Objective-C, в нем тоже есть и классы и наследования, но отличий полно и от Java и от C++. Нельзя говорить, какая моделей правильней, но мне лично кажется наличие релизации по умолчанию большим плюсом, это, кроме упомянутых mixins, дает возможность создания Rich Interfaces, где конечному пользователю вашей API не придется реализовать всю кучу методово интерфейса, но реализовать парочку, на которые опирается остальная часть интерфейса.
Например
interface foo {
default void bar() { baz(); }
void baz();
}
Ну и самое главное, все это не приводито к тому аду, что был с множественным наследованием в С++.
Например
interface foo {
default void bar() { baz(); }
void baz();
}
Ну и самое главное, все это не приводито к тому аду, что был с множественным наследованием в С++.
Все хотят немного haskell в себе, а получается java…
Отлично. Рад что Java развивается. Только не понятно одно, зачем давать возможность интерфейсу что-то делать?
Поддержка лямбд напрашивалась сама собой. Теперь наконец-то можно будет сделать на Java нормальную реализацию LINQ-подобной обработки данных. Очень удобно!
А вот описание тела методов в интерфейсах меня, мягко говоря, удивило. Ведь идеологически интерфейс — это контракт, публичная сторона, которая никак не должна быть связана с конкретной реализацией. По этой причине в C#, например, в интерфейсе нельзя указывать модификаторы доступа или объявлять поля. Зачем смешивать понятия?
А вот описание тела методов в интерфейсах меня, мягко говоря, удивило. Ведь идеологически интерфейс — это контракт, публичная сторона, которая никак не должна быть связана с конкретной реализацией. По этой причине в C#, например, в интерфейсе нельзя указывать модификаторы доступа или объявлять поля. Зачем смешивать понятия?
Так вроде появилась LINQ подобная — filter, mapTo. Но всеже так просто сделать свою реализацию LINQ не получится, т.к. в C# это делается еще за счет функций расширения, которыми по сути и является весь LINQ и yield, который позволяет строить быстро итераторы
В java 8 интерфейсе тоже нельня указывать модификаторы (только public, но все и без него public) и объявлять поля (только static final).
очень легко оступиться и поставить <= место < или начать с 1 вместо 0
Вы, верно, шутите?
В шарпе отказался от использования List.ForEach. LINQ — хорошо, но List.ForEach — это другое, оно делает код хуже читаемым.
Cам MS отказался от метода List.ForEach в Windows Store из тех же соображений.
Еще return перестает работать (выходит не из внешней функции, а из самого ForEach)
минус за что?
Я в упор не понимаю, за что меня минусуют.
Вот такой код компилируется:
А вот такой нет:
Это я и хотел сказать. return из F() больше нельзя сделать.
Вот такой код компилируется:
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() больше нельзя сделать.
Мне кажется это вполне логично, там же return из вложенной функции, с чего бы return должен делаться из F?
Не путаете с генераторами, yield и т.п.?
Если вы экземпляр Action<T> из вашего примера передадите снаружи, а не определив его на месте, return в нем тоже должен по вашему завершать объемлющую функцию?
Я не уверен насчет всех языков, но ни в Scala, ни в Python ни в JavaScript, ни в Java оператор return так во вложенных функциях не работает. И не должен ни в коем случае.
Я не уверен насчет всех языков, но ни в Scala, ни в Python ни в JavaScript, ни в Java оператор return так во вложенных функциях не работает. И не должен ни в коем случае.
Вы бы проверили для начала. И ссылку мою открыли. Я знаю, о чем говорю. 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.Если же я лямбду передам наружу и там её вызову, то вылетит ошибка.
Ладно, насчет Scala — да, там свой хитрый оператор return ибо язык функциональный + функция есть выражение (да и вообще return использовать не рекомендуют, рекомендуют использовать правило последнего оператора).
Но это не меняет того факта что в императивных языках с функциональными элементами такое поведение абсолютно нелогично, потому что тело функция может определять произвольную последовательность действий, а не «формулу» для получения результата.
Но это не меняет того факта что в императивных языках с функциональными элементами такое поведение абсолютно нелогично, потому что тело функция может определять произвольную последовательность действий, а не «формулу» для получения результата.
Хватит бросаться словами «абсолютно нелогично» и «не должен ни в коем случае». Это просто ваше мнение. Если я заменяю цикл foreach на вызов метода ForEach (или наоборот), то мне бы хотелось иметь то же самое поведение метода return. Иначе мой код просто перестанет работать.
Что делать если я хочу просто выйти из функции анонимной? А меня выкинет из той, в которой эта анонимная функция вызывается…?
Я могу задать вам обратный вопрос. Что делать, если я хочу выйти из внешней функции? Понимаете, тут tradeoff между консистентностью кода (код с closure должен работать так же, как и код без closure) и привычностью/ограничениями конструкций языка. Оба решения имеют свои плюсы и минусы.
PS. Если уж совсем приспичило выйти из анонимной функции, то можно использовать goto
PS. Если уж совсем приспичило выйти из анонимной функции, то можно использовать goto
> Если я заменяю цикл foreach на вызов метода ForEach (или наоборот), то мне бы хотелось иметь то же самое поведение метода return.
Почему? Вы заменяете конструкцию языка на вызов метода принимающего функцию как параметр. Почему вы ожидаете одинакового поведения?
Насчет Scala я ошибся, прошу прощения, я редко пользуюсь return-ом в этом языке. В Scala это выглядит более логичным, потому что функция = выражение, выражение = функция. Поэтому ожидать подобного поведения можно (что собственно и описано по приведенной вами ссылке). Но проецировать подобное поведение, характерное для функциональный языков на языки с доминирующей императивностью — неправильно.
Почему? Вы заменяете конструкцию языка на вызов метода принимающего функцию как параметр. Почему вы ожидаете одинакового поведения?
Насчет Scala я ошибся, прошу прощения, я редко пользуюсь return-ом в этом языке. В Scala это выглядит более логичным, потому что функция = выражение, выражение = функция. Поэтому ожидать подобного поведения можно (что собственно и описано по приведенной вами ссылке). Но проецировать подобное поведение, характерное для функциональный языков на языки с доминирующей императивностью — неправильно.
А методы расширения не планируют вводить?
вот убить бы за default реализацию интерфейса…
Что не так?
обоснуйте
потому что 1) это ломает изначальную концепцию интерфейса, как контракта, 2) Это могут и будут использовать неправильно, 3) Теперь это не интерфейс, а абстрактный класс.
Не совсем, интерфейсы не могут иметь состояние и я не согласен что это ломает концепцию интерфейса как контракта.
И да, действительно, это могут использовать неправильно, однако, как говорится: «with great power comes great responsibility».
И да, действительно, это могут использовать неправильно, однако, как говорится: «with great power comes great responsibility».
2) Это могут и будут использовать неправильно
Кому надо, тот будет пользоваться правильно. А быдлокодеры, не умеющие применять ООП, пусть и дальше не умеют его применять.
Согласен. Чем тогда он будет отличаться от абстрактного класса? Множественным наследованием? Так ведь изначально от этого осознанно ушли. И опять вернулись. Тоже не поддерживаю это нововведение. Еще ни разу не было необходимости в дефолтных реализациях методов интерфейса.
Они были нужны как минимум чтобы расширить интерфейсы из стандартной библиотеки. Map, Set и т. д.
А что мешает унаследоваться от этих интерфейсов? Сделать абстрактный класс с необходимой реализацией. При грамотном проектировании таких проблем не возникает.
Мешает то, что уже есть тысячи реализаций этих интерфейсов, которые никто переписывать не будет. Жаль, что когда проектировали эти интерфейсы в 1997-98 году, вы не подсказали им, как сделать грамотно.
Зачем же на личности переходить. И не пойму, как мешают тысячи уже готовых реализаций сделать свою
Зачем нужны новые фичи, чтобы использовать которые, нужно перелопачивать весь старый отлаженный код. Проще уж тогда обойтись без лямбд в коллекциях. Сейчас же как-то обходятся.
Объясняю: было принято решение добавить новые методы в существующие классы в JDK. Пусть это будет метод
Если просто добавить
Предложите другие решения.
foo
в интерфейсе Collection
. Если просто добавить
foo
в Collection
, то вы (программист на Java) больше не сможете скомпилировать свой код, если где-то в вашем коде есть классы, реализующие любую коллекцию. Потому что у них метода foo
. Вам придётся имплементировать метод foo
в ваших реализациях. Поэтому и придуманы дефолтные реализации методов.Предложите другие решения.
То есть, это такой хак, который позволит обратную совместимость обеспечить, а не нововведение, которое кому то там жизнь облегчит.
Как всегда и то и то :) В моём личном понимании — скорее хак, да. Было даже обсуждение, оставить этот хак приватным, то есть разрешить его использовать только в системных пакетах. Потом решили, что другим (прежде всего, разработчиками бибилотке и вообще любых API) это тоже может быть интересным, и сделали фичу публичной.
У меня была такая необходимость, иметь реализацию интерфейса, чтобы вызвать в каком-то нужном порядке методы этого интерфейса, иначе приходилось дублировать код во всех местах.
Например:
Например:
interface Car {
void bump();
void beep();
void run(){ // этот код приходилось реализовывать везде где есть Car
bump();
beep();
}
}
Так вроде если есть желание создать в интерфейсе поведение — сделай абстрактный класс.
Ан нет, т.к. класс, который имплементирует Car уже наследуется от другого абстрактного класса, в который мы не можем добавить Car. Или этот абстрактный класс уже реализован в библиотеке, менять который нельзя/невозможно.
Налицо проблема в проектировании :)
Унаследовать новый абстрактный класс от старого, имплементировать в нем нужный метод и наследовать свои классы уже от него.
Плохое решение: вы избавляетесь от методов в интерфейсе, зато ваш абстрактный класс (до которого может быть не один, не два и не пять уровней наследования) приобретет методы, которые к нему, скорее всего, вообще отношения не имеют. А заодно этот метод получат все классы из других ветвей, которые имели несчастье иметь того же предка.
Не совсем понял мысль.
Мне нужно дефолтную реализацию метода. У меня есть абстрактный класс A,. я наследую от него абстрактный класс B и реализую в нем метод run. В конкретном классе C, унаследованном от B я уже буду иметь эту «дефолтную реализацию».
Какой то другой класс, наследующийся от A, получит сайд-эффект?
Мне нужно дефолтную реализацию метода. У меня есть абстрактный класс A,. я наследую от него абстрактный класс B и реализую в нем метод run. В конкретном классе C, унаследованном от B я уже буду иметь эту «дефолтную реализацию».
Какой то другой класс, наследующийся от A, получит сайд-эффект?
Если C наследуется от B не напрямую, а, например, через D, то метод из интерфейса получат и D, и все его наследники, а не только C, что есть нехорошо.
Кроме того, такой подход вообще не работает для ситуации, когда интерфейс имплементируют классы из совершенно разных иерархий, у которых единственный общий предок —
Кроме того, такой подход вообще не работает для ситуации, когда интерфейс имплементируют классы из совершенно разных иерархий, у которых единственный общий предок —
Object
. У вас по-прежнему в каждом дереве наследования будет своя копия кода для run()
, и если в какой-то момент потребуется изменить поведение этого метода — изменения придется вносить во все имплементации. Методы в интерфейсах позволяют вообще выпилить подобную копипасту из проекта.Откуда возьмется «через D»? Только что C наследовался от A и я специально между ними вклиниваю B.
А эта архитектура с множественными глубокими иерархиями, где реализация интерфейса висит на верхушке, на конкретном классе — нежизнеспособна.
А эта архитектура с множественными глубокими иерархиями, где реализация интерфейса висит на верхушке, на конкретном классе — нежизнеспособна.
Откуда возьмется «через D»
Оттуда же, откуда и A,B и C. Вот взял программист и построил дерево наследования
Никак не привыкну к хаброшорткатам.
Вот взял программист и построил изначально дерево наследования
И имплементировал в C некий интерфейс. Следуя вашей логике, нужно ввести новый абстрактный класс B между A и D, и туда поднять имплементацию интерфейса — на два уровня вверх, где этот контракт вообще не нужен.
Два уровня наследования — это уже глубокая иерархия?
Вот взял программист и построил изначально дерево наследования
abstract A -> D -> C
И имплементировал в C некий интерфейс. Следуя вашей логике, нужно ввести новый абстрактный класс B между A и D, и туда поднять имплементацию интерфейса — на два уровня вверх, где этот контракт вообще не нужен.
с множественными глубокими иерархиями
Два уровня наследования — это уже глубокая иерархия?
И какой толк? Код
run()
все равно придется скопировать.> Чем тогда он будет отличаться от абстрактного класса? Множественным наследованием?
Отсутствием состояния.
> Так ведь изначально от этого осознанно ушли.
Множественное наследование является злом при наличии состояния. При его отсутствии, все становится намного проще, почему его и вернули.
> Еще ни разу не было необходимости в дефолтных реализациях методов интерфейса.
Необходимости на самом деле нет ни в классах, ни в циклах (все можно написать на if/goto и глобальных переменных). Речь об удобстве. Посмотрите еще раз, насколько богаче стали новые интерфейсы коллекций, и насколько удобнее их стало использовать. Теперь представьте себе, что было бы, если бы для каждой конкретной реализации коллекции её автору пришлось бы реализовывать все эти методы самому с нуля.
Отсутствием состояния.
> Так ведь изначально от этого осознанно ушли.
Множественное наследование является злом при наличии состояния. При его отсутствии, все становится намного проще, почему его и вернули.
> Еще ни разу не было необходимости в дефолтных реализациях методов интерфейса.
Необходимости на самом деле нет ни в классах, ни в циклах (все можно написать на if/goto и глобальных переменных). Речь об удобстве. Посмотрите еще раз, насколько богаче стали новые интерфейсы коллекций, и насколько удобнее их стало использовать. Теперь представьте себе, что было бы, если бы для каждой конкретной реализации коллекции её автору пришлось бы реализовывать все эти методы самому с нуля.
Спасибо за развернутый ответ!
У меня при вопросе множественного наследования всегда больше всего вызывало затруднения определить, какой метод будет вызван. Джава мне тем и нравится, что все строго и понятно. Вот интерфейсы, вот классы. Нужна дефолтная реализация или базовое состояние — юзай абстрактный класс. Нужен контракт — юзай интерфейс. Боюсь, что это значительно все усложнит :(
Хотя может быть я и напрасно переживаю, просто непривычно. А как привыкну — станет легко и понятно :)
У меня при вопросе множественного наследования всегда больше всего вызывало затруднения определить, какой метод будет вызван. Джава мне тем и нравится, что все строго и понятно. Вот интерфейсы, вот классы. Нужна дефолтная реализация или базовое состояние — юзай абстрактный класс. Нужен контракт — юзай интерфейс. Боюсь, что это значительно все усложнит :(
Хотя может быть я и напрасно переживаю, просто непривычно. А как привыкну — станет легко и понятно :)
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 дополнительных полезных метода, которые не надо вручную реализовывать.
Более полный обзор со ссылками: www.techempower.com/blog/2013/03/26/everything-about-java-8/
они изобретают С#!
Скорее возвращаются к истокам:
c := #(1 2 3).
x := c ((collect: [:item | item + 1]) select: [:item | item > 2]) inject: 0 into: [:acc :item | acc + item].
c := #(1 2 3).
x := c ((collect: [:item | item + 1]) select: [:item | item > 2]) inject: 0 into: [:acc :item | acc + item].
А C# когда-то изобрели с оглядкой в т.ч. на Java, и это нормально. А по сабжу — все эти прелести дойдут до боевого EE ещё очень нескоро, печаль.
Скорее Scala. Осталось добавить автоматический вывод типов и неявные функции и разницы между ними почти не останется.
В C# нету default методов
Вопрос «чем отличается абстрактный класс от интерфейса?» получил +1 к убойной силе и +5 к троллингу :)
Я ни разу не джавист, но у меня есть, возможно глупый, вопрос относительно реализации методов по-умолчанию в интерфейсах. Это фича только языка Java? Или разработчики добавили какие-то дополнительные конструкции в байткод для её реализации?
Может и добавили, но реализуется это на уровне языка уже давно в той же Scala.
Там invokedynamic появился. И ещё methodHandler и AnonimousClassLoader.
Ну зачем минусовать человека?
Лямбды действительно используют invokedynamic.
Тем не менее, как справедливо заметили выше, можно было бы обойтись и без этого.
Так же, думаю, код может завестись и на старых JVM при передаче соответсвующего значения через флаг
Лямбды действительно используют invokedynamic.
Тем не менее, как справедливо заметили выше, можно было бы обойтись и без этого.
Так же, думаю, код может завестись и на старых JVM при передаче соответсвующего значения через флаг
-target
во время компиляции.Поправочка: реализации JDK от Oracle и OpenJDK в даный момент действительно используют
indy
. Другие реализации вольны делать всё, что им угодно. Например, в процессе компиляции заменять лямбды на семантически эквивалентные конструкции: генерировать статические методы в классе, где используется лямбда или генерить анонимные иннер-классы. И т.д. в том же духе.> очень легко оступиться и поставить <= вместо < или начать с 1 вместо 0
О да, а теперь вообще ни разу не оступишься
> Collections.sort(numbers, (o1, o2)->-o1.compareTo(o2));
(я про ->-)
О да, а теперь вообще ни разу не оступишься
> Collections.sort(numbers, (o1, o2)->-o1.compareTo(o2));
(я про ->-)
Ну только сейчас это синтаксическая ошибка, которую увидит компилятор (в отличие от <= вместо <).
Вы извините, если я сейчас глупость сморожу, я об этих вкусностях Java 8 почти в первый раз слышу, и как и многие читатели топика, жажду узнать больше. Я в топике вижу -> и ->-, соответственно обе их компилятор глотает, и ошибится между ними можно так же как и <= и <.
Collections.sort(numbers, (o1, o2)->-o1.compareTo(o2))
тоже самое что
Collections.sort(numbers, (o1, o2) -> -o1.compareTo(o2))
(перед вызовом compareTo на o1 стоит унарный — (минус)).
Спасибо за разьяснение, теперь понятно.
PS: разделять разные операторы хотя-бы пробелами — хорошая привычка :)
PS: разделять разные операторы хотя-бы пробелами — хорошая привычка :)
И, кстати, использование унарного минуса — не очень хорошая практика в данном случае, лучше написать
o2.compareTo(o1)
для более простого чтения кода. Хотя это скорее вопрос соглашений в компании.дайте структуры и unsigned! в топку эти лямбды. А еще честные дженерики, а не этих уродцев
А что мешает сделать структуру из класса? (Вы ведь подразумеваете аналог struct в С++?)
То что она создестя на куче а не в стеке не важно? Если нет то все ок. Но автору коментария (и многим кому приходится оптимизировать работу с памятью) важно.
В С++ ключевое слово struct имеет другой смысл в отличие от C#. В С++ структуры фактически являются класами только с public полями по умолчанию.
Да и структуру и класс (это одно и тоже вообщем) можно создать как в куче так и на стеке. Эта приятная особенность С++ мне известна. Я намекал SVLad о том что пользователи намного чаще хотят C#-style структур в Java. Особенно те кто пытается снизить нагрузку на GC.
numbers.forEach(System.out::println);
Да-да, мы передали статический метод в качестве функции.
Мы передали обычный метод статической переменной «out».
А я это прощёлкал. Означает ли, что запись в нижеследующем стиле будет валидной?
MyFormatter f = «Hi, %s»;
numbers.forEach(f::format);
MyFormatter f = «Hi, %s»;
numbers.forEach(f::format);
Спасибо, что подметили. Я исправил.
В данном случае победит ближайший в иерархии, то есть интерфейс B. Ну а если хочется использовать метод интерфейса А, то нужно явно указать
A.super.hello();
A.hello() или B.super.hello() вроде бы, или я что-то путаю?
Base64, как долго компилятор нам выдавал грозные сообщения об устаревшем sun.misc.Base64 и приходилось пользовать апачевские библиотеки для простой, казалось бы, вещи.
Всегда использовал javax.xml.bind.DatatypeConverter, зачем апачевские библиотеки?
Жалко только, что пока эти все плюшки дойдут до Android — я уже состариться успею :)
Ну а вообще, конечно неплохо бы убирать лишнюю писанину как в Scala, когда можно написать var и тип выведется по выражению справа. Также и; — уже давно ненужный элемент языка. Я бы этого очень хотел для любимого языка — большей выразительности, меньше кода, при этом читабельность на уровне.
А плюшки Java 8 — гуд, особенно Streams.
Ну а вообще, конечно неплохо бы убирать лишнюю писанину как в Scala, когда можно написать var и тип выведется по выражению справа. Также и; — уже давно ненужный элемент языка. Я бы этого очень хотел для любимого языка — большей выразительности, меньше кода, при этом читабельность на уровне.
А плюшки Java 8 — гуд, особенно Streams.
Особенно параллельные streams.
На мой взгляд var и всякие похожие фичи делают код плохо читаемым. Не понятно какого типа переменная, намного сложнее искать ее создание, особенно если оно скрыто за кучей методов. Поэтому я категорически против такого.
Опыт C# показывает, что как раз наооборот var делает код более читаемым. Ведь и так понятно var x = new SomeObject(); что x будет типа SomeObject. Так же если создается фабрикой, от фабрики примерно ясно, что за объект. Вот в случаях когда не ясно по коду, какой тип возвращает функция, тогда тип, конечно, желательно указывать явно для повышения читаемости кода.
ничего не понятно в случае: var x = MyClass::getSomeMagicMethod(); А если таких вызовов много, приходится постоянно лезть внутрь этих методов.
Зачем мне для читаемости кода знать тип переменной? Это лишняя информация.
var x = point.getX()+2;
var y = point.getX()-3;
if(x==y) {
//...
}
Если get'ы возвращают целый тип, то все хорошо, если дробный — у вас тут кучка потенциальных багов, связанных с точностью вычислений. Не зная типов, вы, скорее всего, пропустите это место — в самом деле, сложили, отняли и сравнили, где тут может быть баг?
Меня, скорее всего, минусуют те, кто пишут настолько хуевый код, что им приходится указывать тип всех переменных, чтобы понять его. Потому что я отлично читаю код и без явного указания типов. Я что особенный какой-то?
Когда код пишешь, проблем с типом переменных нет. А вот когда заходишь в чужую функцию во время отладки, явный тип переменных помогает.
Я вам еще раз повторяю. Убираете типы переменных, пишете нормальный код с нормально именованными методами и переменными, и весь код отлично читается. Если вы без явного типа переменных не можете понять код, значит он написан криво.
А в любом нормальном отладчике тип переменной всегда можно посмотреть при необходимости.
А в любом нормальном отладчике тип переменной всегда можно посмотреть при необходимости.
В динамических языках вообще типы нигде не указываются. Там функции могут возвращать все что угодно. И что — люди справляются же.
Вместо var можно использовать продвинутую IDE, которая сама подставляет тип переменной при объявлении. При этом в коде будет видно, что за тип переменной используется.
чувствую запах перла…
Может, было бы правильнее использовать в статье слово «функциональный» вместо «декларативный»?
Не соглашусь. Декларативный — значит пишем код и говорим что мы хотим сделать, а не как именно. Когда мы итерируем коллекцию и в цикле что-то делаем — мы определяем механику перебора. Используя декларативный подход — то говорим: примени метод/функцию/выражение ко всем элементам.
Посмотри как пишется код на функциональных языках (я лично непосредственно имел дело только с F#, но это вполне себе типичный функциональный язык) и на декларативных (QML, Prologue, HTML :) ) и поймешь, что таки в статье используется именно первый вариант. Хотя википедия говорит, что «декларативный ЯП» имеет 2 значения, одно из которых — любой неимперативный язык.
Сортировка потенциально бесконечного stream'а — вот что страшно…
Итератор тоже может быть «потенциально бесконечным». И что?
На нем нельзя вызвать sort.
А на stream — можно.
А на stream — можно.
Конечно для безконечного множества никто стабильной сортировки гарантировать не может (только если это не уже заведомо отсортированное множество).
Что нам говорит API:
Похоже момент оговорен. Я думаю (не исследовал) что сортировка происходит по частям, что и не гарантирует общей правильности результата.
Что нам говорит API:
For ordered streams, the sort is stable. For unordered streams, no stability guarantees are made.
Похоже момент оговорен. Я думаю (не исследовал) что сортировка происходит по частям, что и не гарантирует общей правильности результата.
В данном случае победит ближайший в иерархии, то есть интерфейс BПочему B? Ведь C реализует и A тоже.
Sign up to leave a comment.
Что Java 8 нам готовит