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 .