Skip to the content.

Технологии программирования

Назад к списку лекций

Generics. Optional. Collections

Про Generics после компиляции.

Компилятор не генерирует class-файл для каждого параметризованного типа. Он создаёт один class-файл для дженерик-типа. В Java в скомпилированном байт-коде нет никакой информации о типе параметра. Один байт-код для всех типов параметра.

Компилятор стирает информацию о типе, заменяя все параметры без ограничений (unbounded) типом Object, а параметры с границами (bounded) — на эти границы. Это называется type erasure

class Box<T> {
    private T item;

    public void putItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }
    
    public T toArray(T[] arr) {
    }
}

public class Temp {
    public static void main(String[] args) {
        Box<String> box = new Box<>();
        box.putItem("");
    }
}

Такой наш класс после компиляции превратится в следующий:

class Box {
    private Object item;

    public void putItem(Object item) {
        this.item = item;
    }

    public Object getItem() {
        return item;
    }
}

class Temp {
    public static void main(String[] args) {
        Box box = new Box();
        box.putItem((Object) "");
        String str = (String) box;
    }
}

Поэтому в домашке про generics в методе toArray не получится создать new T[]() - потому что в runtime java просто не будет ничего знать о типе T

Подробнее можно прочитать в этой статье

Важный вывод который нужно запомнить:

По своей сути generic-класс – это обертка над классом с Object, которая проверяет (приводит) тип во всех точках входа и выхода из generic-кода. Тем самым проверяется корректность присваиваний и операций на этапе компиляции

Optional

Один из важных дженерик-классов, который мы используем - это Optional.

Цель класса - предоставить решение на уровне типа для представления необязательных значений вместо нулевых ссылок Иными словами - это некоторая обертка над любым типом T, которая позволяет нам представить отсутствие значение не с помощью null (нулевой ссылки), а с помощью объектов данного класса и методов данных объектов. Это позволяет явным образом в коде отметить, что в какой-то переменной может отсутствовать значения и нужно явно учесть случай отсутствия значения.

import java.util.Optional;

class Temp {
    public static void main(String[] args) {
        Integer tempInt = null;
        Optional<Integer> a = Optional.of(tempInt); // Рассчитывает, что не передаем в него null. 
                                                    // Если передать null, то будет exception
     
        Optional<Integer> optionalInt = Optional.ofNullable(tempInt); // Можно передать null
        Optional<Integer> emptyOptional = Optional.empty(); // Сразу пустой Optional
     
        optionalInt.isEmpty(); // если null, или если мы создали пустой optional -> true
        optionalInt.isPresent();
        
        Integer value = optionalInt.get(); // если значения нет - то будет exception
                                           // поэтому мы хотим проверить, есть ли в Optional значение:
        if (optionalInt.isPresent()) {
            value = optionalInt.get();
        } else {
            value = 0;
        }
        Integer value = optionalInt.orElse(0);

        // Пример, где нам это может потребоваться. Например, при работе с базой данных.
        // Если мы пишем библиотеку, которую потом будут использовать другие разработчики (коллеги, например),
        // и какой-то метод в нашей библиотеки пытается что-нибудь достать из базы данных, то мы должны предусмотреть 
        // ситуацию, когда искомый объект отсутствует в базе. Конечно в такой ситуации мы можем просто вернуть null, 
        // но было хорошим тоном явно указать в сигнатуре метода, что мы можем ожидать в ответе отсутствие объекта:
        
        Optional<Student> optionalSt = getFromDb(1);
        
        // Для любителей c# - Optional в каком-то смысле аналог 'int?'
    }
    
    public static Optional<Student> getFromDb(int id) {
        // какой-то код который вытаскивает из бд пользователя
        // student - либо null, или Student
        return Optional.ofNullable(student);
    }
    
    
    public class Student {
        // код студента
    }
}

Java Collection Framework

alt text

Collection Простые наборы элементов.

Базовый набор методов (size(), isEmpty(), add(E e) и др - см доку)

В интерфейсе уже есть дефолтные реализации.

Map

Работа с типами данных ключ - значение. В c# - аналог Dictionary, в питоне - словари.

Базовый набор методов (containsKey(Object key), get(Object key), isEmpty(), size())

Map

alt text

import java.util.HashMap;
import java.util.Hashtable;

class Temp {
    public static void main(String[] args) {
        // Hashtable - хэш-таблица. Нельзя использовать null как value или key. 
        // Все методы помечены synchronized - поэтому есть проблемы с производительностью
        Hashtable<Integer, String> table = new Hashtable<>();
        table.put(1, "hello");
        String value = table.get(1); // hello

        // HashMap - альтернатива. Не синхронизирована. И можно использовать null
        HashMap<Integer, String> map = new HashMap<>();
        map.put(1, "good evening");
        String str = map.get(1); // good evening

        // LinkedHashMap - упорядоченная в порядке добавления элементов.

        // TreeMap - основана на красно-черных деревьях, тоже является упорядоченной. 
        // Порядок можно задавать через Comparator

        // WeakHashMap. Garbage Collector автоматически удалит элемент из коллекции 
        // при следующей сборке мусора, если на ключ этого элемента нет жёстких ссылок.


    }
}

List

alt text

import java.util.ArrayList;
import java.util.LinkedList;

class Temp {
    public static void main(String[] args) {
        // Vector - синхронизирован. Будем использовать ArrayList
        // Stack - LIFO, но лучше использовать ArrayDeque

        // ArrayList - список основан на массиве
        ArrayList<String> ourList = new ArrayList<String>();
        ourList.size();
        ourList.add("");
        ourList.remove(0);
        ourList.toArray(new String[]{});
        ourList.addAll(new ArrayList<>());

        // LinkedList - связный
    }
}

alt text

import java.util.HashSet;

class Temp {
    public static void main(String[] args) {
        // HashSet - Set, базирующийся на HashMap. Порядок не гарантируется. 
        HashSet<String> set = new HashSet<>();
        set.add("123");
        set.add("123");
        set.add("123");
        set.size();
        set.remove("123");

        // LinkedHashSet - базируется на LinkedHashMap

        // TreeSet - специфичный сет, можно управлять порядком элементов и использовать Comparator
    }
}

Queue

alt text

import java.util.ArrayDeque;

class Temp {
    public static void main(String[] args) {
        // PriorityQueue - очередь FIFO, не поддерживает null, можно управлять порядком 
        // через Comparator

        // ArrayDeque - LIFO (stack), быстрее чем Stack и LinkedList
        ArrayDeque<String> stack = new ArrayDeque<>();
        stack.addFirst("123");
        stack.pollFirst(); // возвращает первый элемент и удаляет из коллекции 
    }
}

Более распространенные коллекции: HashMap, HashSet, ArrayList, LinkedList, ArrayDeque