Обобщения Java: отношение is-A к объекту

Я пытаюсь понять особенности универсального класса и его конкретных экземпляров, а также особенности универсальной коллекции.

Предположим, я определил универсальный класс: Stack<T> сейчас я пытаюсь использовать это так:

Stack<String> ts = new Stack<>(5);
Stack<Object> to = new Stack<>(5);
to = ts // compilation error!

так очевидно Stack<String> это не Stack<Object>,

Я запутался, когда определил универсальную коллекцию:

Set<E> elemnts = new Hashset<>();

и заметил, что я могу использовать elements.toString()! это означает, что Set<E> elemnts продолжается Object! так что на самом деле здесь происходит?

4 ответа

Решение

Так что же такое дженерики в Java? Как уже упоминал Дэйв, универсальные типы являются принудительными для компилятора. Точнее, во время выполнения универсальные типы больше не существуют. Они заменены Object или верхняя граница универсального типа. Этот процесс называется стиранием типа. Таким образом, универсальные типы существуют только во время компиляции.

Тогда почему мы не можем бросить ArrayList<String> в ArrayList<Object> ? Хотя параметры типа находятся в отношении (String это Object), общие классы - нет. Позвольте мне привести простой пример. Если

ArrayList<String> strings = new ArrayList<String>();
ArrayList<Object> objects = strings;

было бы возможно, если один звонит

objects.add(new Object());

что должно произойти? поскольку strings может только держать String с, Java не может "положить" Object в этот список. Но с другой стороны, если вы посмотрите на декларацию objects нет ничего намекающего на то, что это не должно работать. Поэтому Java не допускает преобразование между универсальными классами, даже если универсальные типы находятся в отношении.

Но универсальные типы не присутствуют во время выполнения. Так в чем суета? Должен ArrayList<String> на самом деле не быть ArrayList<Object> во время выполнения и, следовательно, все это не должно иметь значения? Ну нет. Поскольку вы знаете тип во время компиляции, вы можете использовать определенные контракты. Например, при извлечении элемента из ArrayList<String> можно быть уверенным, что этот извлеченный объект имеет public char charAt(int) метод (так как String есть этот метод). Если по какой-либо причине это ArrayList<String> вернет Object вместо String не мог назвать charAt(int) метод.

Ошибка компиляции не означает, что String не является Objectчто не соответствует действительности. Например, следующий код работает просто отлично:

String s = "I am String!";
Object o = s;

Что ошибка означает, что Stack<String> не является Stack<Object>, Например, вы можете позвонить push(new Object()) на Stack<Object>но явно не на Stack<String>,

Строковый класс неявно является подклассом Object. Поскольку класс Object является суперклассом всех классов в Java. Поэтому нижеприведенное утверждение действительно

String x="hello";
Object o= x;

Но Stack<String> не подкласс Stack<Object>, Поэтому код дает ошибку времени компиляции.

Stack<String> ts = new Stack<>(5);
Stack<Object> to = new Stack<>(5);
to = ts // compilation error!

Для следующих:

Stack<String> ts = new Stack<>(5);
Stack<Object> to = new Stack<>(5);
to = ts // compilation error!

Да, эта компиляция не удалась, потому что обобщенные элементы - это, по сути, функция компилятора, обеспечивающая безопасность типов. На самом деле это всего лишь один класс стеков в отношении виртуальной машины.

Это утверждение:

Set<E> elemnts = new Hashset<>();

Эквивалентно:

Set<E> elemnts = new Hashset<E>();

Нотация была введена в Java 7 и иногда называется "оператором алмаза" из-за <>, Это просто чтобы удалить избыточность.

Это класс Set, который расширяет Object, как и все классы в Java, расширяют объект.

Следующее справедливо для объекта Class в вашем коде:

 boolean b = ts.getClass() == to.getClass();  // b is true
Другие вопросы по тегам