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

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

Выделенный список языковых функций из других языков, которые я хотел бы видеть в Java. Помечен как java, xtend, цейлон.

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

Это первый пост в серии постов. Я решил разделить их на части для удобства чтения.

Xtend – действительно интересный язык. Он считает себя расширением Java и фактически компилируется в исходный (!) код Java. Xtend предлагает множество новых идей. Вот подборка моих личных ярких моментов.

Синтаксический сахар для Геттеров и сеттеров

Мы все знаем геттеры и сеттеры в Java. Мы все знаем, зачем они нам нужны, и мы также все знаем, что иногда они могут причинять боль, особенно на стороне абонента. Почему я не могу просто написать obj.name вместо obj.getName () и позволить компилятору разобраться с остальным? Что ж, это именно то, что вы можете сделать с Xtend. Если он сталкивается с доступом участника (например, obj.name ) сначала он проверяет, есть ли соответствующий открытый член, и если его нет, он ищет метод get property () и вызывает его. Это становится еще лучше, если вашим свойством является число, потому что тогда вы можете использовать obj.value++ и другие, которые будут скомпилированы в подходящие конструкции getter/setter. Простой, простой, эффективный. Это полностью функция компилятора, нет необходимости где-либо изменять существующий байт-код или время выполнения. Почему Java не делает этого сегодня, для меня загадка.

Методы расширения

Библиотека Java Runtime library действительно довольно хороша. Однако традиционно эта библиотека, как известно, довольно… консервативна в том, что она предлагает. “Если вы сомневаетесь, оставьте это”, по-видимому, было руководящим принципом. Следствием этого является то, что у нас есть целая армия “библиотек производительности”, которые предлагают печально известные классы XYUtils (например, StringUtils , IOUtils …). Наиболее известными из них являются пакеты Apache Commons , а также Google Guava . Эти помощники великолепны с точки зрения функциональности, но синтаксис немного нелогичен. Например, StringUtils.leftPad(myString, 10) намного менее интуитивно понятен, чем myString.левая накладка(10) , а также в первом случае программисту необходимо знать о существовании СтрингУтилы , , тогда как во втором случае завершение кода IDE просто сообщит им. Опять же, это полностью синтаксический вопрос. Компиляторы спешат на помощь! Xtend позволяет импортировать статический метод как расширение (вот документация

import static extension java.util.Collections.*; // note the 'extension' keyword

// ... later in your code ...

myList.max(); // compiles to Collections.max(myList)

Пока что Xtend – единственный язык, который я когда-либо видел, в котором эта функция была реализована правильно. Вот как вы справляетесь с утилитой. Это может показаться смехотворно незначительным изменением, но как только вы привыкнете к этому, вы не сможете без этого обойтись. Для java , это действительно был бы низко висящий плод. Я бы даже сделал еще один шаг вперед и удалил расширение ключевое слово. Любой статический метод с соответствующей сигнатурой в моей текущей области должен иметь право на использование в качестве метода расширения, при этом методы, определенные для объекта, имеют приоритет в случае столкновения имен.

Разговор с классами без .class

Одной из самых больших (синтаксических) болевых точек при написании отражающего кода является тот факт, что вы постоянно пишете .class , например , в MyClass.class.getName() . Ну, да. Конечно хочу поговорить об объекте класса, когда я пишу MyClass . Так почему я вообще должен писать .класс здесь? Потому что синтаксическому анализатору требуется на один взгляд вперед меньше? Ну же. Xtend и здесь помогает. Достаточно просто написать MyClass.getName() , и он компилируется в MyClass.class.getName() . Конечно, My Class.getName() также может ссылаться на общедоступную статическую строку getName() метод, определенный в классе. В Extend этот статический метод будет иметь приоритет (вы можете указать доступ в Xtend, чтобы сделать его явным). Но давайте будем честны здесь: мне еще предстоит определить свой первый public static Field[] метод getDeclaredFields() в любом классе Java. Еще одно небольшое изменение, еще один шаг к счастью разработчика.

Всемогущая депеша

Существует проблема с диспетчеризацией методов в Java (и множестве других языков, включая C #), которая в 99% случаев остается незамеченной, но когда это произойдет, это действительно вызовет у вас головную боль. Представьте, что у вас есть несколько перегрузок такого метода, как этот:

public void print(Person p) { ... }

public void print(Student s) { ... }

public void print(Object o) { ... }

(Давайте предположим, что Студент Студент Человек ). Быстрый тест: какая перегрузка вызывается следующим кодом:

Object me = new Student();
print(me);

Ответ таков: print(объект o) . Поскольку Java выбирает перегрузку метода для вызова на основе статического типа (т.Е. типа переменной, в данном случае Object ), а не динамического (тип фактического значения, хранящегося в переменной, в данном случае Студент ). В основном это делается для повышения эффективности, и в большинстве случаев это называется “единая отправка”. Но что, если вы хотите другого случая? Вы должны были бы сделать это:

public void print(Object o) {
  if(o instanceof Student) {
    print((Student)o);
  }else if(o instanceof Person) {
    print((Person)o);
  }else{
    // print the object like before
  }
}

Этот код реализует то, что называется “двойной отправкой”, и написание этого кода – это боль . Это повторяющееся и в целом то, что, по моему мнению, должен делать компилятор. Кроме того, вам нужно позаботиться о том, чтобы сначала проверить наиболее конкретный тип. В Xtend все, что вам нужно сделать для достижения описанного выше поведения, – это использовать ключевое слово dispatch :

public dispatch void print(Person p) { ... }

public dispatch void print(Student s) { ... }

public dispatch void print(Object o) { ... }

……… который будет скомпилирован точно в instanceof -cascade в print(Object) , как описано выше. Спасибо вам, компилятор, за то, что избавили меня от необходимости печатать!

Ceylon – это язык JVM, то есть язык, который компилируется в байтовый код JVM. У него много функций (некоторые из них спорны), но у него также есть некоторые драгоценные камни в наборе языковых инструментов.

Ввод потока

Потоковая типизация – это функция, которая в настоящее время используется многими языками (привет, TypeScript !), Но именно на Цейлоне я впервые увидел эту функцию в ее полной форме, и я был поражен. Легко объяснить, что он делает. Мы все написали подобный код на Java:

for(Object element : myCollection){
   if(element instanceof Dog) {
      ((Dog)element).bark()
   } else {
      // ... do something else
   }
}

Вам когда-нибудь приходило в голову почему вы должны опускаться до Собаки в приведенном выше примере после проверки того, что элемент действительно имеет тип |/Dog ? Это повторяющееся, это часто означает введение новых переменных (для которых вы должны выбрать правильное имя ...), и в целом это просто раздражает писать. На Цейлоне приведенное выше приведение не является необходимым . Вообще . Как только вы подтвердите, что объект, хранящийся в переменной, относится к определенному классу с помощью проверки типа, компилятор будет обрабатывать переменную как этот тип , пока вы находитесь внутри условного блока. Нам даже не нужен новый синтаксис для этой функции. Мы просто избавляемся от шума. Впервые увидеть это на практике было для меня как по волшебству – я никогда не знал, что хочу этого, но теперь я хочу, и я хмурюсь по поводу каждого броска вниз, который я должен сделать после проверки instanceof .

Обнуляемость и строгое обнуление через ?

Вы знаете, что первое, что я делаю в 99% реализаций моих методов на Java? Это происходит примерно так…

public void save(User u){
   if(u == null){
      throw new IllegalArgumentException("User must not be NULL!");
   }
   // do ACTUAL work
}

Это широко считается хорошей практикой, и так оно и есть. Отслеживание значения null в обратном направлении по кодовой базе в отладчике совсем неинтересно. Однако в приведенном выше методе я указываю, что я хочу Пользователь экземпляр. Строго говоря, null – это не User (что вы можете сказать, потому что null instanceof User фактически возвращает false в Java). И все же вызов save(null) является законным в Java. Цейлон предлагает элегантный способ исправить это. По умолчанию вы не можете передавать null в качестве аргумента , за исключением случаев, когда аргумент специально допускает значение null . Это делается путем добавления ? к параметру:

public void save(User? u){ ... }  // accepts nulls
public void save(User u){ ... }  // compiler rejects (potential) null values

Преимуществом здесь является то, что во втором случае мне, как программисту, не нужно выполнять ручное null -проверка правильности вообще. Компилятор делает это во время компиляции. Таким образом, этот метод не только безопаснее, он также быстрее во время выполнения и улавливает ошибки в самый ранний возможный момент времени (фаза компиляции). Беспроигрышный вариант.

Отражение, проверенное компилятором

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

    value d = `value Movie.year`;
    print( d.name); //prints "year"

В этом случае Movie – это класс. Мы рефлекторно обращаемся к полю Фильм.год . Эквивалентом Java будет Movie.getClass().getField("год") . Так чем же Цейлон лучше? Хорошо, если Фильм на самом деле не имеет поля с именем год , компилятор будет жаловаться . Это меняет правила игры для рефлексивного кодирования. Сколько раз вы переводили шаблоны в спящий режим, которые не работали из-за неправильного написания имени поля? Или потерян во время рефакторинга? Вот и ты.

На этом заканчивается первая часть этой серии. Я надеюсь, вам понравилось и, возможно, вы узнали о нескольких новых языковых функциях. Xtend и Ceylon могут предложить много других вещей, и я настоятельно рекомендую проверить их, по крайней мере, для получения опыта, если не для производства.

(Вторая часть сейчас находится в Сети.)

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