В сервисе метод принимает массив Object, а ему передают массив String; объясните, почему это разрешает компилятор, но запись числа может завершиться исключением.
Массивы в Java ковариантны: массив String является подтипом массива Object, поэтому такую передачу разрешает компилятор. Однако во время выполнения массив сохраняет реальный тип String[], и попытка записать в него Integer завершается ArrayStoreException.
Ковариантность массивов была заложена в Java как удобный способ использовать массивы конкретных типов там, где ожидается массив базового типа. Например, массив строк можно безопасно читать как массив Object, поскольку каждая строка является Object.
Чтобы сохранить типобезопасность при изменяемости массивов, JVM хранит реальный тип компонента массива и проверяет каждую запись. Поэтому ошибка обнаруживается во время выполнения, а не маскируется как корректная запись.
Ссылка типа Object[] описывает доступный интерфейс переменной, но не меняет тип самого объекта. Если объект создан как String[], ссылка Object[] всё равно указывает именно на String[].
Неверное предположение о том, что тип ссылки полностью определяет допустимые записи, приводит к ArrayStoreException. Это особенно опасно на границах API, принимающих Object[], поскольку вызывающий код может передать массив более узкого типа.
Присваивание values = names корректно благодаря ковариантности массивов. Чтение элемента через values также безопасно: любой элемент String[] действительно является Object.
При записи JVM проверяет, совместим ли фактический объект с компонентным типом массива. Integer несовместим с String, поэтому операция прерывается исключением ArrayStoreException.
Это отличается от обобщённых коллекций: List<String> нельзя присвоить List<Object>, потому что обобщённые типы в Java инвариантны. Такая инвариантность обнаруживает потенциально опасное присваивание уже на этапе компиляции.
Ковариантность массивов удобна для чтения, но создаёт риск при записи. Если API должен принимать элементы разных типов, лучше использовать массив, фактически созданный как Object[], либо коллекцию с подходящей параметризацией.
Допустим, универсальный компонент принимает Object[] и добавляет туда результаты обработки. Передача ему заранее созданного String[] формально разрешена, но компонент может получить ArrayStoreException при добавлении любого нестрокового значения.
Вариант оставить контракт Object[] без изменений прост и совместим с существующим API, но он безопасен только при строгом контроле фактического типа массива. Вариант копировать входной массив в новый Object[] устраняет риск записи в исходный массив, но добавляет расход памяти и времени.
Если API проектируется заново, предпочтительнее принимать коллекцию с явно определённой семантикой или возвращать новый массив Object[]. Когда сохранение исходного массива обязательно, выбранным решением должна быть проверка и документирование допустимых типов элементов; для универсального добавления — создание массива именно типа Object[].
Ответ: нет. Приведение меняет только тип ссылки, через которую доступен объект. Реальный объект остаётся String[], поэтому проверка записи продолжает использовать String как компонентный тип.
Ответ: любой элемент String[] обязан быть String, а String является Object, поэтому результат чтения можно представить как Object. При записи Object может оказаться экземпляром типа, не являющегося String, поэтому JVM обязана выполнить проверку и при несовместимости выбросить ArrayStoreException.
Ответ: List<Object> разрешал бы добавить в список Integer, что нарушило бы обещание List<String>. Поэтому параметризованные типы коллекций инвариантны. Для безопасного чтения разных списков применяется, например, List<? extends Object>, но такая ссылка не позволяет добавлять произвольные объекты.