...

четверг, 8 мая 2014 г.

Типы-значения в Java

Этот пост — вольно-краткий перевод документа State of the Values, предложения по введению типов-значений в JVM и сам язык Java, который написали Джон Роуз, Брайан Гоетц и Гай Стил, разбавленный моими мыслями. Опущены детали предложения по реализации типов-значений на уровне байт-кода, что не очень интересно для большинства Java-программистов.




Важнейшая мысль: объектная (ссылочная) идентичность нужна только для поддержки изменяемости объектов. Объект может изменить состояние, но по ссылке мы всегда можем проверить, что это «тот же» объект. Поэтому типы-значения будут строго неизменяемые.

Предполагаемые варианты использования типов-значений:



  • Числа: комплексные, беззнаковые, 128-битные, с фиксированной запятой и т. д. Все, что не описывается имеющимися примитивами.

  • Алгебраические типы: Optional<T>, Choice<T,U>, перечисления (enum)

  • Кортежи (пары, тройки, ...)

  • Итераторы, курсоры

  • Кортежи (пары, тройки, ...)

  • «Уплощение» сложных структур.




Целевые фичи:


  • Можно использовать типы-значения везде, где сейчас можно объекты и примитивы: локальные переменные, поля, элементы массива, аргументы методов.

  • В обычной ситуации аллокация в регистрах, на стеке, поле-значение это не ссылка, а прямо байты полей в родительском объекте. Хотя у ВМ есть право таки создать значение в куче, напр. если она считает, что так будет лучше, не может иначе обеспечить атомарность, не хватает стека, и т. д. Массивы с элементами типа-значения должны быть «плоскими».

  • При необходимости (напр. при передаче в метод, который хочет Object) неявные приведение (boxing) к обычному объекту-двойнику (как сейчас с примитивами).

  • Не просто структурки с полями: можно объявлять методы как в обычном объекте, переопределять equals(), toString(). Вызов метода не (обязательно) приводит к приведению к обычному объекту. Инкапсуляция полей возможна.

  • Компилятор или ВМ генерирует equals(), hashCode(), toString(), compareTo() по полям, если не переопределены в коде.

  • Типы-значения сами (а не только их «обычные» объектные двойники) могут реализовывать интерфейсы. Т. е. передача в метод, который хочет, допустим, Comparable не обязательно приводит к оборачиванию значения в объект.

  • Можно объявлять поля типа-значения как volatile.

  • Типы-значения могут содержать и обычные объектные поля (которые не обязаны быть рекурсивно неизменяемыми, как сами значения), и поля других типов-значений. Но: не могут содержать поля своего же типа.


Ограничения (должно быть запрещено или вызывать оборачивание в объект-двойник):



  • Тип-значение не может наследовать ни классу, ни другому типу-значению, от типа-значения ничего нельзя наследовать. (Хотя, возможно, будет возможность наследовать от «чисто абстрактных» классов, например от java.lang.Number.)

  • Вызов wait(), notify(), clone() или finalize() на значении.

  • Вызов System.identityHashCode() к значению.

  • Присвоение переменной типа-значения null.

  • Приведение к Object или любому супертипу.

  • Применение рефлексии к значению.

  • Запарки с атомарностью (напр. на платформе нельзя обеспечить атомарность чтения/записи, если общий размер типа-значения больше машинного слова, или двойного слова).


Синтаксис



Объявление типа-значения максимально приближено к обычным классам:

final __ByValue class Point {
public final int x;
public final int y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}

public boolean equals(Point that) {
return this.x == that.x && this.y == that.y;
}
}




Уродливое __ByValue специально для того, чтобы никто не воспринимал это как окончательный синтаксис. Может, будет аннотация типа @ValueType. Объявление полей финальными — либо обязательно, либо неявное, как сейчас методы в интерфейсах можно объявить без public, но они все равно будут публичными. То же касается и модификатора класса.

Создание значения:



Point p = __MakeValue(x, y);



Тут вместо __MakeValue будет либо название типа без new, либо ничего, т. е. создание значения — просто скобочки с аргументами.

Поддержки специальных литералов (типа 0 + 1i для комплексных чисел или 1u для беззнаковых), а также перегрузки операторов (что было бы полезно тоже в первую очередь для числовых типов), скорее всего, не будет.




Надо понимать, что массивы, java.lang.String, java.lang.Integer и т. д. для бинарной совместимости в типы-значения преобразованы не будут. Так что существующий код магически не ускорится. По крайней мере, не в разы. Внутренний тип java.util.HashMap.Entry, например, поменять вполне могут.


Возможно, сделают отдельную сущность для массивов-значений, которые могут заменить varargs на уровне языка.


















ОбъектЗначение
Содержит что-то разноеОбъектТипы-значения
ОдинаковоеМассивыМассивы-значения?

Generics



Самый важный вопрос. В текущем виде дженерики — фича времени компиляции, и никаких типов-значений они поддерживать не будут. Но раз уж сказал «А» (типы-значения), то говори и «Б», поэтому рано или поздно дженерики переделают.

Мой прогноз:



  • Для Java 9 уже есть одна «главная» фича — модуляризация, Oracle явно делает на нее ставку, вероятность увидеть ее в Java 9 близка к 100%.

  • Если в Java 9 дженерики оставят «как есть», шанс увидеть типы-значения в этой версии 30%.

  • Если решат выкатить типы-значения и обновленные дженерики одновременно, в лучшем случае это случится в Java 10.




Я знаю, этот блог читает много сотрудников Oracle, которые куда внимательнее следят за списками рассылки и вообще ближе к теме, буду рад если они внесут важные уточнения.

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий