Перейти к основному содержимому

Обобщенный вывод целевого типа в Java

· 4 мин. чтения

1. Введение

Определение типа было введено в Java 5 в дополнение к обобщениям и было существенно расширено в следующих выпусках Java, что также называется обобщенным выводом целевого типа.

В этом руководстве мы рассмотрим эту концепцию с помощью примеров кода.

2. Дженерики

Обобщения предоставили нам множество преимуществ, таких как повышенная безопасность типов, избежание ошибок приведения типов и общие алгоритмы. Подробнее о дженериках можно прочитать в этой статье .

Однако введение дженериков привело к необходимости написания шаблонного кода из-за необходимости передавать параметры типа . Некоторые примеры:

Map<String, Map<String, String>> mapOfMaps = new HashMap<String, Map<String, String>>();
List<String> strList = Collections.<String>emptyList();
List<Integer> intList = Collections.<Integer>emptyList();

3. Вывод типов до Java 8

Чтобы уменьшить ненужную многословность кода, в Java был представлен вывод типов, который представляет собой процесс автоматического вывода неуказанных типов данных выражения на основе контекстной информации.

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

Мы можем увидеть тот же код, используя новую концепцию:

List<String> strListInferred = Collections.emptyList();
List<Integer> intListInferred = Collections.emptyList();

В приведенном выше примере на основе ожидаемых возвращаемых типов List<String> и List<Integer> компилятор может вывести параметр типа для следующего универсального метода:

public static final <T> List<T> emptyList()

Как видим, получившийся код получился лаконичным. Теперь мы можем вызывать универсальные методы как обычные методы, если можно вывести параметр типа.

В Java 5 мы могли бы делать вывод о типах в определенных контекстах, как показано выше.

Java 7 расширила контексты, в которых это могло выполняться. Он представил алмазный оператор <> . Подробнее об алмазном операторе вы можете прочитать в этой статье .

Теперь мы можем выполнить эту операцию для конструкторов универсального класса в контексте присваивания. Один из таких примеров:

Map<String, Map<String, String>> mapOfMapsInferred = new HashMap<>();

Здесь компилятор Java использует ожидаемый тип назначения для вывода параметров типа в конструктор HashMap .

4. Обобщенный вывод целевого типа — Java 8

Java 8 еще больше расширила возможности вывода типов. Мы называем эту расширенную возможность вывода обобщенным выводом целевого типа. Вы можете прочитать технические подробности здесь .

В Java 8 также появились лямбда-выражения. Лямбда-выражения не имеют явного типа. Их тип выводится, глядя на целевой тип контекста или ситуации. Target-Type выражения — это тип данных, который компилятор Java ожидает в зависимости от того, где появляется выражение.

Java 8 поддерживает вывод с использованием Target-Type в контексте метода. Когда мы вызываем универсальный метод без явных аргументов типа, компилятор может просмотреть вызов метода и соответствующие объявления метода, чтобы определить аргумент типа (или аргументы), которые делают вызов применимым.

Давайте рассмотрим пример кода:

static <T> List<T> add(List<T> list, T a, T b) {
list.add(a);
list.add(b);
return list;
}

List<String> strListGeneralized = add(new ArrayList<>(), "abc", "def");
List<Integer> intListGeneralized = add(new ArrayList<>(), 1, 2);
List<Number> numListGeneralized = add(new ArrayList<>(), 1, 2.0);

В коде ArrayList<> явно не предоставляет аргумент типа. Таким образом, компилятор должен вывести это. Сначала компилятор просматривает аргументы метода add. Затем он просматривает параметры, переданные при различных вызовах.

Он выполняет анализ логического вывода о применимости вызова , чтобы определить, применим ли метод к этим вызовам . Если из-за перегрузки применимо несколько методов, компилятор выберет наиболее конкретный метод.

Затем компилятор выполняет анализ вывода типа вызова , чтобы определить аргументы типа. Ожидаемые типы целей также используются в этом анализе . Он выводит аргументы в трех экземплярах как ArrayList<String> , ArrayList<Integer> и ArrayList<Number> .

Вывод Target-Type позволяет нам не указывать типы для параметров лямбда-выражения:

List<Integer> intList = Arrays.asList(5, 2, 4, 2, 1);
Collections.sort(intList, (a, b) -> a.compareTo(b));

List<String> strList = Arrays.asList("Red", "Blue", "Green");
Collections.sort(strList, (a, b) -> a.compareTo(b));

Здесь параметры a и b не имеют явно определенных типов. Их типы выводятся как Integer в первом лямбда-выражении и как String во втором.

5. Вывод

В этой быстрой статье мы рассмотрели вывод типов, который вместе с дженериками и лямбда-выражением позволяет нам писать краткий код Java.

Как обычно, полный исходный код можно найти на Github .