Рубрики
Без рубрики

Модернизация Java – Список пожеланий по языковым функциям (Часть 2)

В этой второй части серии “Модернизация Java” мы рассмотрим языковые возможности C#, Jav… Помеченный java, javascript, ruby.

В этой второй части серии “Модернизация Java” мы рассмотрим языковые возможности C#, JavaScript (ES6) и других. Первую часть серии можно найти здесь.

Ruby – это язык сценариев, особенно хорошо известный благодаря веб-фреймворку “ruby on rails”. Это довольно чистый язык для языков сценариев, и это был первый язык, на котором я столкнулся с выходом ключевое слово и концепция сопрограммы. В принципе, выход позволяет вам выйти из текущего потока управления функцией, и когда она будет вызвана в следующий раз, вы продолжите с того места, на котором остановились:

// this is how coroutines could look in Java
public Iterator powersOfTwo(){
   int current = 1;
   while(true){
      yield current;  // note the new "yield" keyword here
      current *= 2;
   }
}

Приведенный выше пример представляет собой генератор для бесконечной последовательности. Обратите внимание, что мы не записываем циклы процессора с помощью нашего цикла while(true) здесь. Поскольку мы выходим из потока управления на каждой итерации, для каждого вызова "оператор.next() выполняется только одна итерация. Возвращаемый итератор является неявным , вам не нужно его определять. Эта концепция также была адаптирована ES6, Python, C# и многими другими языками, и люди широко используют ее (привет, Redux Saga !). Как и многие другие функции в этой серии блогов, это улучшение качества жизни, и его можно “эмулировать” в стандартной Java. Однако я действительно думаю, что это было бы очень полезно.

Программисты часто называют C# “следующей эволюцией Java”. Действительно, два языка имеют много общего, и если бы не различия в стандартной библиотеке, вполне возможно, можно было бы написать транспилятор, который преобразует файл исходного кода C # в файл исходного кода Java и наоборот. Полномасштабное обсуждение выходит за рамки этой статьи. C# предлагает ряд интересных языковых функций, которых нет в Java.

Частичные классы

В C# вы можете пометить класс как частичный . Это позволяет разделить один класс на несколько файлов, но компилятор рассматривает их как один:

// in file "myClassPart1.cs"
public partial class MyClass {

}

// in file "myClassPart2.cs"
public partial class MyClass {

}

Это отличается от инструкции import , потому что, в конце концов, в двоичных файлах есть только один класс. “Зачем кому-то понадобилось это делать? “вы можете спросить. “вы можете спросить. Основная причина, по которой это полезно, – генерация кода. “вы можете спросить. Основная причина, по которой это полезно, – генерация кода. Например, существуют мощные разработчики пользовательского интерфейса WYSIWYG, которые создают файлы исходного кода C# (например, один из них интегрирован в VisualStudio). “вы можете спросить. Основная причина, по которой это полезно, – генерация кода. Например, существуют мощные разработчики пользовательского интерфейса WYSIWYG, которые создают файлы исходного кода C# (например, один из них интегрирован в VisualStudio). Если вам когда-либо доставляло сомнительное удовольствие иметь дело с генерацией кода, то вы познаете боль от необходимости вручную редактировать автоматически сгенерированные файлы. “вы можете спросить. Основная причина, по которой это полезно, – генерация кода. Например, существуют мощные разработчики пользовательского интерфейса WYSIWYG, которые создают файлы исходного кода C# (например, один из них интегрирован в VisualStudio). Если вам когда-либо доставляло сомнительное удовольствие иметь дело с генерацией кода, то вы познаете боль от необходимости вручную редактировать автоматически сгенерированные файлы. Проблема в том, что как только вы повторно запустите генератор, ваши ручные изменения будут потеряны. В мире Java предпринимались попытки “пометить” разделы рукописного кода как таковые, чтобы генератор оставил их в покое (см., Например, средства генерации кода EMF ). С частичными занятиями эти боли исчезают навсегда. Генератор управляет одним файлом (одной частью класса), в то время как ваш рукописный код переходит в совершенно другой файл, который просто является другой частью того же класса. Вы можете быть уверены, что ваши рукописные изменения не будут перезаписаны каким-либо автоматическим генератором, поскольку они находятся в другом файле, о котором генератор не знает. Это функция, которая касается только компилятора Java, среда выполнения остается нетронутой, потому что в итоге создается только один файл *.class . Java является популярной мишенью для генерации кода, и наличие частичных классов помогло бы значительно облегчить работу с сгенерированным кодом.

Ключевое слово события

Это сравнительно небольшая деталь C#, но мне лично нравится: ключевое слово событие . Как часто вы писали подобный код на Java:

private Set eventListeners= new HashSet<>();

public void registerEventListener(EventListener listener){
   this.eventListeners.add(listener);
}

public void removeEventListener(EventListener listener){
   this.eventListeners.remove(listener);
}

public void fireEvent(Event event){
   for(Listener listener : this.eventListeners){
      listener.onEvent(event);
   }
}

Это действительно однообразно. Если у вас есть класс, который имеет дело с 5 различными классами событий, то приведенный выше код необходимо продублировать и адаптировать еще четыре раза. В C# вы получаете весь приведенный выше код следующим образом:

public event MyEvent MyEvent;

Если вы хотите добавить прослушиватели событий:

myClass.MyEvent += myListener;

… и запустить событие внутри компании:

this.MyEvent(event);

Послушай, ма, не зацикливайся! Это действительно мелочь, но она избавляет от большого количества шаблонного кода. Является ли использование шаблона наблюдателя в целом хорошей идеей или нет, это совершенно другое обсуждение.

Кортежи

В последней версии C# была добавлена встроенная поддержка кортежей. Это позволяет вам легко создавать, передавать и деконструировать пары, тройки, четверки, как вы их называете. Вот как это выглядит:

(int count, double sum, double sumOfSquares) = ComputeSumAndSumOfSquares(sequence);

Что здесь произошло? Вычислить сумму и сумму квадратов возвращает тройку, содержащую количество, сумму и сумму квадратов. Мы получаем все три значения в одном вызове метода. В случае, если нас не интересует ни один из этих трех, мы можем заменить объявление переменной на _

(_, double sum, _) = ComputeSumAndSumOfSquares(sequence);

Это просто, это элегантно, жаль, что этого не существует в Java.

имя

Хорошая привычка программирования – писать предварительные условия, чтобы убедиться, что параметры, которые вы получаете, действительно соответствуют спецификации. Это позволяет вашим методам быстро отказывать и предоставлять точные сообщения об ошибках. Теперь, если вы рассмотрите этот код:

public long sum(Iterator values){
   if(values == null) { throw new IllegalArgumentException("Argument 'values' must not be NULL!"}
   // ...
}

… вы заметите, что значения появляется дважды : один раз в качестве имени параметра и один раз внутри строкового литерала. Это прекрасно само по себе, но что произойдет, если я переименую переменную? Строковый литерал не изменится, потому что IDE не знает о семантической корреляции между ними (вы также можете включить замену внутри строк, но у этого есть другие проблемы …). C# предлагает элегантное решение:

public long Sum(IEnumerator values){
   if(values == null) { throw new ArgumentException("Argument '" + nameof(values) + "' must not be NULL!"}
   // ...
}

Как вы можете видеть, имя устраняет необходимость жестко кодировать имена переменных в строковые литералы. | имя выдает

В ES6 есть несколько очень удобных улучшений для JavaScript, касающихся синтаксиса.

В ES6 есть несколько очень удобных улучшений для JavaScript, касающихся синтаксиса. Деконструкция объекта

В ES6 есть несколько очень удобных улучшений для JavaScript, касающихся синтаксиса. Деконструкция объектов Один из наиболее полезных из них называется деконструкция объектов . В ES6 есть несколько очень удобных улучшений для JavaScript, касающихся синтаксиса. Деконструкция объектов Один из наиболее полезных из них называется

MethodResult result = someMethod();
int size = result.size();
byte[] data = result.getData();
User author = result.getAuthor();

В ES6 есть несколько очень удобных улучшений для JavaScript, касающихся синтаксиса. Деконструкция объектов Один из наиболее полезных из них называется || деконструкция объектов || . Как часто 6 устраняет здесь много церемоний: d вы пишете такой код на Java:

const { size, data, author } = someMethod();

В ES6 есть несколько очень удобных улучшений для JavaScript, касающихся синтаксиса. Деконструкция объектов Один из наиболее полезных из них называется деконструкция объектов . Как часто 6 устраняет здесь много церемоний: d вы пишете такой код на Java: это похоже на кортежи C #, но не совсем то же самое. В ES6 есть несколько очень удобных улучшений для JavaScript, касающихся синтаксиса. Деконструкция объектов Один из наиболее полезных из них называется

Неявное преобразование из объекта в логическое значение

При написании кода JavaScript, как бы я ни ненавидел неявные преобразования в целом, есть одна конструкция, которую мне нравится использовать:

if(this.header){
   // render header
}

Обратите внимание, что заголовок в приведенном выше коде не является логическим значением, это структура данных. Используя его в операторе if , мы проверяем, равно ли оно нулю (или не определено , но это уже другая история). Это неявное преобразование из объекта в логическое значение путем проверки пустоты, безусловно, полезно. Однако в JavaScript есть некоторые проблемы, когда дело доходит до работы с числовыми значениями, поскольку число 0 также неявно преобразуется в false ; соглашение, которое, на мой взгляд, никогда не должно было выходить за рамки языков низкого уровня, таких как C. Проверка на пустоту – очень распространенная задача в Java, и сделать ее более быстрой и простой кажется хорошей идеей.

Вы когда-нибудь сталкивались с ситуацией в Java, когда вы хотите написать кэш настраиваемого размера (в мегабайтах)? Что ж, тогда у тебя большие проблемы. В Java вы не знаете, насколько велик объект на самом деле есть. Обычно вам не нужно беспокоиться, но если вы столкнетесь с таким поворотным случаем, эти проблемы вернутся с удвоенной силой. Вы можете оценить размер объекта с помощью отражения, но это медленная и дорогостоящая операция. В качестве альтернативы вы можете использовать инструментарий Java через агента, но это усложняет развертывание вашего приложения и в целом кажется неправильным , учитывая, что вы хотите сделать что-то простое, например, измерить размер объекта в памяти. То, что я действительно хотел бы видеть в Java, – это то, что C/C ++ предоставляет “из коробки”, а именно размер ключевое слово. Я понимаю, что это непростая задача в JVM, но для программистов, пишущих “клиентов” на JVM, это практически невозможно.

Haskell – функциональный язык и во многих отношениях духовный преемник OCaml.

Понимание списка

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

[(i,j) | i <- [1,2], j <- [1..4] ]

… будет производить пары [(1,1),(1,2),(1,3),(1,4),(2,1),(2,2),(2,3),(2,4)] . Попробуйте сделать это с вложенными циклами for, и вы поймете, почему приведенный выше синтаксис великолепен.

Частичное Применение

В Haskell вы можете частично применять функции, создавая новые в процессе. Например:

add x y = x + y
addOne = add 1
add 3 4 -- produces 7
addOne 6 -- also produces 7

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

BiFunction add = (a,b) -> a + b;
Function addOne = (a) -> add(1, a);

… за исключением того, что вам нужно намного больше церемоний. Это также похоже на функцию bind в JavaScript и параметры значений по умолчанию (встречаются на нескольких языках). Несмотря на то, что частичное применение наиболее широко используется в функциональном программировании, это аспект, который легко “извлечь”, поскольку он не зависит от других характеристик функционального программирования (таких как ленивая оценка). Теоретически это работает на любом языке, который допускает функцию (или метод, или процедуру, или …) звонки. У меня нет объяснения, почему эта изящная функция так мало используется.

Я надеюсь, что вам понравился этот тур по языковым особенностям. Java – очень хороший язык во многих отношениях, но ему необходимо продолжать развиваться. В этой серии блогов я попытался дать обзор того, “что делают все остальные”. Я пропустил что-то важное? Существуют ли какие-либо другие языковые функции, которые вы хотели бы видеть в Java, которые вообще не рассматривались в этой серии? Дайте мне знать в комментариях:)

Спасибо за чтение!

Оригинал: “https://dev.to/martinhaeusler/modernizing-java—a-language-feature-wish-list-part-2-7c0”