Сучасна диджитал-освіта для дітей — безоплатне заняття в GoITeens ×
Mazda CX 5
×

Рассуждения по поводу ArrayStoreException

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

Добрый день всем. Может быть вопрос больше подходит для stackoverflow, но все таки попытаюсь спросить здесь.
Вопрос, собственно, имеет более «философский». А состоит он в следующем. Рассмотрим пример кода:

 class A {...};
    class B extends A {...};
     
    //in main:
     
    B[] x1 = new B[10];
    A[] x2 = x1;
     
    x2[0] = new A();

Стандарт разрешает подобное присваивание. Компилятор не выдает никаких ошибок. Но! В процессе выполнения мы получаем ArrayStoreException. Не понятен такой ньюанс: почему компилятор пропускает такое? Почему нельзя отлавливать эту ошибку на моменте компиляции? Или может такой «финт» используется где-то в других моментах?

👍ПодобаєтьсяСподобалось0
До обраногоВ обраному0
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Дима, как с тобой можно выйти на связь? Потеряла твой номер, если актуально напиши мне: 097 473 38 10, или мейл [email protected]

Конечно с коментов поулыбался. Ответ очевиден и на поверхности:
Всё в Java является объектами. Во всём понимании объектно-ориентированного программирования. И переменные — фактически ссылки на эти объекты, ничего больше.
x1 является ссылкой на массив объектов типа B. Притом массив — это не указатель на память, а именно указатель на объект. Который знает, что он является массивом. Объектов типа B.

x2 после присваивания также является ссылкой. На тот же самый объект. Массив типа B. И когда этому объекту говорят «получи-распишись объект типа А», он с гордостью заявляет — «мы таких не держим».

Аналогичный пример — переопределение методов. Если в классе-наследнике метод переопределён (например toString), то когда к нему обращаются по ссылке класса-предшественника (например Object) — будет вызван именно переопределённый метод класса наследника.

Никакой логики в этом нет. Просто создатели Java посчитали, что для пользы программиста работать должно именно так — и вот оно так работает. В других языках встречаются другие реализации. IMHO такая — единственно верная с точки зрения реализации человеческой логики.

Before java 1.5 there were no generics. So covariante arrays was some kind of generic code in previous versions of java. Such as: Collections.sort(Object[] a) {...} Now we have generics in java, so covariant arrays left just for backward compatability. This is why we can’t check this errors at compile time. As a result ArrayStoreException is just defense from incorrect programms. Your programm fails as soon as possible, during storing incorrect value inside array, not during iteration or whatever else.

В «правильном» мире разные типы массивов должны быть неприводимы друг к другу. В Jav’е это было сделано потому, что generic’ов ещё не было, а generic-функции на массивах нужны (например копирование или сортировка). Поэтому разрешили «приводить вверх». С другой стороны, это породило проблему с присвоением. Но это было меньшее зло.

Коментар порушує правила спільноти і видалений модераторами.

почему компилятор пропускает такое?

А почему не должен. Компилятор и анализатор кода — это разные вещи.

Почему нельзя отлавливать эту ошибку на моменте компиляции?

Есть класс А и интерфейс И один из методов которого возвращает масив экземпляров А и 2 либы. В первой возвращается Б (наследник А), во второй В (наследник А), но интерфейсно они возвращают А[].
Что произойдет если я загружу эти либы в рантайме (например по имени)?

В общем такая ошибка больше логическая.

У джавы довольно быстрый компилятор. А то что вы хотите — это даже не ошибка, а предупреждение, при том с условно сложной логикой. Такие вещи должны отлавливать статические анализаторы кода, ИМХО.

Еще один момент. В моем мире массивы не нужны, все пользуются коллекциями с дженериками :)

Еще один момент. В моем мире массивы не нужны, все пользуются коллекциями с дженериками :)

Именно поэтому жабапрограммы так неистово жрут память(ArrayList<integer> жрет более чем в 3 раза больше чем int[])

Использование и оптимизация памяти в джава программах — тема, конечно, интересная (если есть мысли откройте еще одну, более профильную, тему), но ваш пример немного оффтоп.

но ваш пример немного оффтоп.

Не более офтопная чем твоя реплика про колекции.

Не более офтопная чем твоя реплика про колекции.

все пользуются коллекциями с дженериками

Контекст: контроль типов

Есть и другой контекст:

В моем мире массивы не нужны

Я хотел сказать что твой мир программистов пишущих пожирающие ресурсы программы не исчерпывает весь мир джава разработки.

Ну и на твое предложение об открытии нового топика, безотносительно того возражаешь ты или нет, уж извини, я положу с большим прибором.

Ты прав, в 3 раза я погорячился, давай посчитаем: итак каждый элемент ArrayList-a это object reference который на 64-битной джвм занимает 8 байт, дальше собственно Integer который состоит из 4 байт собственно полезных данных + 8 байт оверхеда на джавовский Object, всего получается 20 байт, т.е. в 5 раз больше размера int которые содержат полезные данные.

>Есть класс А и интерфейс И один из методов которого возвращает масив экземпляров >А и 2 либы. В первой возвращается Б (наследник А), во второй В (наследник А), но >интерфейсно они возвращают А[].

>Что произойдет если я загружу эти либы в рантайме (например по имени)?

Довольно смутно могу представить зачем такое может понадобиться? (не в Вашу критику сказано, а в отсутствии собственного опыта). А вобще было бы замечательно, если бы Вы по этому поводу еще и кусочек кода привели (с абстрактными библитетками, конечно).

Но в общем случае, я так понял, что это все придумано для хитрой реализации интерфейсов?

Ок, пример проще

/**
* Ваш код
*/
public class Main {
public static void main(String[] args) {
A[] data = new DataInitializer().initData();
data[0] = new A();
}
}
-----------------------------------
/**
* Общая библиотека
*/
public class A {

}
public interface Initializer {
A[] initData();
}
-----------------------------------
/**
* Подключаемая библиотека, например драйвер БД
* Класс B — не публичный, он скрыт внутри реализации библиотеки
*/
class B extends A {

}
public class DataInitializer implements Initializer {
@Override
public A[] initData() {
return new B[10];
}
}

Дима, здравствуй. Я потеряла твой номер. Если актуально — выйди со мной на связь: 097 473 38 10, мейл [email protected], фейсбук: Yana Lanevska

Підписатись на коментарі