Что такое ковариантность массивов в Java?

Ответ

Ковариантность массивов в Java — это особенность языка, при которой массив типа S[] (где S — подтип T) может быть присвоен ссылке типа T[]. Это означает, что массивы сохраняют отношение наследования своих компонентных типов.

Пример:

String[] stringArray = {"hello", "world"};
Object[] objectArray = stringArray; // Ковариантность: String[] является Object[]

Проблема и проверка во время выполнения: Ковариантность массивов не является типобезопасной на этапе компиляции. Компилятор разрешит присвоить objectArray[0] = new Integer(123), так как Integer — это Object. Однако это нарушит фактический тип массива (String[]). Поэтому JVM выполняет проверку типа при каждой записи в массив (Runtime Type Check), которая выбросит ArrayStoreException.

objectArray[0] = 123; // ArrayStoreException во время выполнения!

Сравнение с дженериками: В отличие от массивов, дженерики в Java инвариантны по умолчанию (List<String> не является List<Object>), что обеспечивает полную типобезопасность на этапе компиляции. Ковариантность для дженериков можно выразить явно через ? extends (List<? extends Number>).

Ответ 18+ 🔞

А, ну это же классика, блядь! Ковариантность массивов в Java — это такой старый, пиздец, подлянка, которую они в язык впихнули ещё на заре времён, когда про дженерики только в страшном сне думали.

Представь себе, ёпта, такую картину. У тебя есть массив строк, ну, String[]. И язык тебе говорит: «Да похуй, чувак, можешь спокойно присвоить его переменной типа Object[], потому что строка — это же объект, ёбана!». Это и есть ковариантность, блядь: если String — дитё Object, то и String[] — дитё Object[]. Логично же, да? На первый взгляд.

String[] stringArray = {"hello", "world"};
Object[] objectArray = stringArray; // Вот она, сука, ковариантность! Всё компилируется.

И вроде бы красота, блядь. Но тут-то и начинается пиздец, нахуй! Потому что компилятор, такой доверчивый мудак, разрешит тебе через эту новую ссылку objectArray засунуть туда что угодно, что является Object.

objectArray[0] = 123; // Integer — тоже Object, ёпта! Компилятор молчит.

А JVM в этот момент, блядь, уже чувствует подвох. Она же знает, что на самом деле этот массив — String[], а не какой-то там Object[]. И в момент записи, прямо во время выполнения, она хватает тебя за руку и орёт: «Куда, сука?! Это же массив строк, ёбаный в рот!». И швыряет в тебя ArrayStoreException. Проверка типа на лету, блядь, при каждой записи! Вот такой костыль, ебать мои старые костыли, пришлось ввести, чтобы эта «удобная» ковариантность не развалила всё к ебеням.

А потом, блядь, придумали дженерики, умные стали. И там уже сделали по-нормальному: List<String> — это НЕ List<Object>. Инвариантность, блядь! Полная типобезопасность на этапе компиляции. Хочешь ковариантности? Изволь, блядь, явно указать: List<? extends Number>. Тогда компилятор тебя уже не пустит ничего писать в такой лист, кроме null. И никаких сюрпризов во время выполнения, пиздец!

Вот и получается, что ковариантность массивов — это такой исторический артефакт, хитрая жопа, которая тянет за собой проверки в рантайме. А дженерики — это уже осмысленный, типобезопасный подход. Так что когда видишь ArrayStoreException, вспоминай эту весёлую историю, как в Java пытались сделать жизнь «проще», а получилось как всегда, в рот меня чих-пых.