← Назад к вопросам

Как разработчики писали код до появления Generics

1.7 Middle🔥 121 комментариев
#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как разработчики писали код до появления Generics (Java 5)

Do Java 5 (2004) не было Generics. Это было времечко дикого полиморфизма и постоянных приведений типов.

1. Использование Object

Всё было Object — это был единственный "обобщённый" способ.

// Pre-Generics (Java < 5)
public class Stack {
    private Object[] elements;
    private int size;
    
    public void push(Object o) {
        elements[size++] = o;
    }
    
    public Object pop() {
        return elements[--size];  // Возвращаем Object
    }
}

// Использование
Stack stack = new Stack();
stack.push("Hello");
stack.push(123);
stack.push(new Date());

// Проблема: не знаешь что внутри
Object value = stack.pop();
String text = (String) value;  // Нужно приводить тип
int number = (int) stack.pop();  // Может быть ClassCastException!
Date date = (Date) stack.pop();  // Или это String?

2. Обязательные приведения типов

// Было опасно
public void dangerousCode() {
    List list = new ArrayList();  // Без типа!
    
    list.add("String");
    list.add(123);
    list.add(new Date());
    
    // Берём из списка
    for (int i = 0; i < list.size(); i++) {
        String str = (String) list.get(i);  // Может упасть!
        // Runtime Error: ClassCastException если не String
    }
}

3. Дублирование кода

// Нужно было писать отдельный класс для каждого типа
public class StringStack {
    private String[] elements;
    private int size;
    
    public void push(String o) {
        elements[size++] = o;
    }
    
    public String pop() {
        return elements[--size];
    }
}

public class IntStack {
    private int[] elements;
    private int size;
    
    public void push(int o) {
        elements[size++] = o;
    }
    
    public int pop() {
        return elements[--size];
    }
}

public class DateStack {
    private Date[] elements;
    private int size;
    
    public void push(Date o) {
        elements[size++] = o;
    }
    
    public Date pop() {
        return elements[--size];
    }
}

// Огромное дублирование кода! (DRY violation)

4. Collections API без типизации

// Pre-Generics collections — кошмар
public void collectionsNightmare() {
    List users = new ArrayList();  // Нет типа
    users.add(new User("John"));
    users.add("Not a user!");  // Никто не помешает
    users.add(123);
    
    // Нужно проверять тип перед использованием
    for (int i = 0; i < users.size(); i++) {
        Object obj = users.get(i);
        if (obj instanceof User) {  // Нужна проверка!
            User user = (User) obj;
            System.out.println(user.getName());
        } else {
            System.out.println("Not a user: " + obj);
        }
    }
}

5. Отсутствие type safety на compile time

// Это компилируется!
public List getUserList() {
    return new ArrayList();  // Может содержать ЧТО УГОДНО
}

public void useUserList() {
    List users = getUserList();
    users.add("String");  // Компилятор ничего не скажет
    
    User user = (User) users.get(0);  // Упадёт в runtime!
    // ClassCastException: String cannot be cast to User
}

6. Map без типизации

// Pre-Generics
public Map getUserCache() {
    return new HashMap();  // Ключи и значения могут быть любые
}

public void cacheNightmare() {
    Map cache = getUserCache();
    
    cache.put("key1", new User("John"));
    cache.put(123, new User("Jane"));  // Число как ключ
    cache.put("key2", "Not a user");  // String как значение
    
    // При доступе нужны приведения типов
    User user = (User) cache.get("key1");
    String notUser = (String) cache.get("key2");  // Может ошибиться
    User user2 = (User) cache.get(123);  // или это null?
}

7. Решение: instanceof checks везде

// Pre-Generics код был полон instanceof проверок
public void processObjects(List objects) {
    for (int i = 0; i < objects.size(); i++) {
        Object obj = objects.get(i);
        
        if (obj instanceof String) {
            String str = (String) obj;
            System.out.println("String: " + str);
        } else if (obj instanceof Integer) {
            Integer num = (Integer) obj;  // Autoboxing не было!
            int n = num.intValue();  // Нужно распаковывать
            System.out.println("Number: " + n);
        } else if (obj instanceof User) {
            User user = (User) obj;
            System.out.println("User: " + user.getName());
        }
    }
}

8. Примеры реального кода Pre-Generics

// Collections API до Java 5
public void oldCollectionsAPI() {
    // Vector — синхронизированный List (был до ArrayList)
    Vector v = new Vector();
    v.addElement("item1");
    v.addElement(new Integer(42));
    Object item = v.elementAt(0);
    String str = (String) item;  // Приведение
    
    // Enumeration вместо Iterator
    Enumeration e = v.elements();
    while (e.hasMoreElements()) {
        Object obj = e.nextElement();  // Object, не типизировано
        System.out.println(obj);
    }
    
    // Hashtable вместо HashMap
    Hashtable hash = new Hashtable();
    hash.put("key", "value");
    String value = (String) hash.get("key");  // Приведение
}

9. Частые ошибки и баги

// 1. ClassCastException в runtime
public User getUser(List list, int index) {
    return (User) list.get(index);  // Может упасть!
}

// 2. Потеря информации о типе
public List getUserList() {
    List list = new ArrayList();  // Какие объекты внутри? Неизвестно
    // Вызывающий код должен знать и полагаться на документацию
    return list;
}

// 3. Смешивание типов в коллекции
List list = new ArrayList();
list.add("string");
list.add(123);
list.add(new User());
// Какой тип ожидаем? Нужно проверять каждый элемент

// 4. Игнорирование типов
public void careless(Object obj) {
    obj.toString();  // Работает для всех
    // ((User) obj).getName();  // А это может упасть
}

10. Появление Generics в Java 5 (2004)

// Java 5+ — революция!
public class Stack<T> {  // Generic type параметр T
    private T[] elements;
    private int size;
    
    public void push(T o) {
        elements[size++] = o;
    }
    
    public T pop() {
        return elements[--size];  // Возвращаем T, не Object
    }
}

// Использование — type safe
Stack<String> stringStack = new Stack<>();
stringStack.push("Hello");
String result = stringStack.pop();  // Нет приведения типов

Stack<Integer> intStack = new Stack<>();
intStack.push(42);
int value = intStack.pop();  // Nет приведения типов

// List с типизацией
List<User> users = new ArrayList<>();  // Type safe
users.add(new User("John"));
users.add("Not a user");  // Compile error!

for (User user : users) {  // Enhanced for loop
    System.out.println(user.getName());
}

11. Почему не было Generics раньше

Причины:

  1. Type erasure — Generics удаляются при компиляции, совместимость с pre-Java 5
  2. Backward compatibility — нужно было поддерживать старый код
  3. JVM limitations — JVM не знает о Generics, только Java компилятор
  4. Сложность реализации — требовал больших изменений в компилятор
// Type erasure: Generics теряются в runtime
public <T> void printType(List<T> list) {
    // list.getClass() вернёт List, не List<String>
    System.out.println(list.getClass());  // class java.util.ArrayList
    // T информация потеряна в runtime
}

12. Наследие Pre-Generics

// Рас старый код жив, приходится жить с raw types
@SuppressWarnings("rawtypes")  // Подавляем warning
public void legacyCode() {
    List list = new ArrayList();  // Raw type — плохая практика
    list.add("item");
    
    List<String> typedList = new ArrayList<>();
    typedList.addAll(list);  // Работает, но с warning
}

Резюме

Pre-Generics (Java < 5):

  • Всё было Object
  • Обязательные приведения типов
  • Дублирование кода
  • Нет type safety
  • Много instanceof проверок
  • ClassCastException в runtime

Post-Generics (Java 5+):

  • Type parameters <T>
  • Compile-time type checking
  • Нет дублирования
  • Type safe collections
  • Меньше ошибок
  • Самодокументирующийся код

Основной урок: Generics были революцией в Java, сделали язык более безопасным и выразительным. Это улучшение инструмента навечно.