Приведение ссылочных типов java

Приведение типов объектов в Java

Обзор приведения типов в Java, покрытый простыми и понятными примерами.

1. Обзор

Система типов Java состоит из двух типов: примитивов и ссылок.

В этой статье мы рассмотрели примитивные преобразования , и здесь мы сосредоточимся на приведении ссылок, чтобы получить хорошее представление о том, как Java обрабатывает типы.

Дальнейшее чтение:

Основы дженериков Java

Оператор Java instanceof

2. Примитивный против Ссылка

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

В обоих случаях мы “превращаем” один тип в другой. Но, упрощенно говоря, примитивная переменная содержит свое значение, и преобразование примитивной переменной означает необратимые изменения ее значения:

double myDouble = 1.1; int myInt = (int) myDouble; assertNotEquals(myDouble, myInt);

После преобразования в приведенном выше примере переменная myInt 1 , и мы не можем восстановить предыдущее значение 1.1 от него.

Ссылочные переменные различны ; ссылочная переменная ссылается только на объект, но не содержит самого объекта.

И приведение ссылочной переменной не затрагивает объект, на который она ссылается, а только помечает этот объект другим способом, расширяя или сужая возможности работы с ним. Приведение вверх сужает список методов и свойств, доступных этому объекту, а приведение вниз может расширить его.

Ссылка подобна дистанционному управлению объектом. Пульт дистанционного управления имеет больше или меньше кнопок в зависимости от его типа, а сам объект хранится в куче. Когда мы выполняем кастинг, мы меняем тип пульта дистанционного управления, но не меняем сам объект.

3. Апкастинг

Приведение из подкласса в суперкласс называется восходящим . Как правило, апкастинг неявно выполняется компилятором.

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

Чтобы продемонстрировать восходящее вещание, давайте определим класс Animal :

Теперь давайте расширим Животное :

public class Cat extends Animal < public void eat() < // . >public void meow() < // . >>

Теперь мы можем создать объект класса Cat и назначить его ссылочной переменной типа Cat :

И мы также можем назначить его ссылочной переменной типа Animal :

В приведенном выше задании имеет место неявное повышение. Мы могли бы сделать это явно:

Но нет необходимости делать явное приведение дерева наследования. Компилятор знает, что cat является Животным и не отображает никаких ошибок.

Обратите внимание, что ссылка может ссылаться на любой подтип объявленного типа.

Используя upcasting, мы ограничили количество методов, доступных для экземпляра Cat , но не изменили сам экземпляр. Теперь мы не можем сделать ничего специфичного для Cat – мы не можем вызвать meow() в переменной animal .

Хотя Cat объект остается Cat объектом, вызов meow() вызовет ошибку компилятора:

// animal.meow(); The method meow() is undefined for the type Animal

Чтобы вызвать meow() нам нужно опустить animal , и мы сделаем это позже.

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

3.1. Полиморфизм

Давайте определим еще один подкласс класса Animal , a Dog :

public class Dog extends Animal < public void eat() < // . >>

Теперь мы можем определить метод feed () , который обрабатывает всех кошек и собак, как животных :

public class AnimalFeeder < public void feed(Listanimals) < animals.forEach(animal ->< animal.eat(); >); > >

Мы не хотим, чтобы AnimalFeeder заботился о том, какое животное находится в списке – кошка или Собака . В методе feed() все они являются животными .

Неявная передача происходит, когда мы добавляем объекты определенного типа в список animals :

List animals = new ArrayList<>(); animals.add(new Cat()); animals.add(new Dog()); new AnimalFeeder().feed(animals);

Мы добавляем кошек и собак, и они неявно переводятся в тип Animal . Каждая Кошка является Животным и каждая Собака является Животным . Они полиморфны.

Кстати, все объекты Java полиморфны, потому что каждый объект, по крайней мере, является Объектом . Мы можем назначить экземпляр Animal ссылочной переменной типа Object , и компилятор не будет жаловаться:

Object object = new Animal();

Вот почему все объекты Java, которые мы создаем, уже имеют Object конкретные методы, например, toString() .

Также распространена передача на интерфейс.

Мы можем создать Новый интерфейс и сделать Cat реализовать его:

public interface Mew < public void meow(); >public class Cat extends Animal implements Mew < public void eat() < // . >public void meow() < // . >>

Теперь любой объект Cat также может быть передан в Mew :

Cat – это Мяу , апкастинг является законным и выполняется неявно.

Это/| Кошка является Новым , Животным , Объектом и Кошкой . В нашем примере он может быть назначен ссылочным переменным всех четырех типов.

3.2. Переопределение

В приведенном выше примере метод eat() переопределен. Это означает, что, хотя eat() вызывается для переменной типа Animal , работа выполняется методами, вызываемыми на реальных объектах – кошках и собаках:

public void feed(List animals) < animals.forEach(animal ->< animal.eat(); >); >

Если мы добавим некоторые записи в наши классы, мы увидим, что вызываются методы Cat ‘и Dog :

web - 2018-02-15 22:48:49,354 [main] INFO com.baeldung.casting.Cat - cat is eating web - 2018-02-15 22:48:49,363 [main] INFO com.baeldung.casting.Dog - dog is eating
  • Ссылочная переменная может ссылаться на объект, если объект имеет тот же тип, что и переменная, или если он является подтипом
  • Апкастинг происходит неявно
  • Все объекты Java являются полиморфными и могут рассматриваться как объекты супертипа из-за восходящей трансляции

4. Даункастинг

Что делать, если мы хотим использовать переменную типа Animal для вызова метода, доступного только классу Cat ? А вот и даункастинг. Это приведение из суперкласса в подкласс.

Мы знаем, что переменная animal относится к экземпляру Cat . И мы хотим вызвать метод Cat ‘s meow() на животном . Но компилятор жалуется, что метод meow() не существует для типа Animal .

Чтобы вызвать мяу() мы должны опустить животное к Кошке :

Внутренние скобки и тип, который они содержат, иногда называются оператором приведения. Обратите внимание, что внешние скобки также необходимы для компиляции кода.

Давайте перепишем предыдущий пример Animal Feeder с помощью метода meow() :

public class AnimalFeeder < public void feed(Listanimals) < animals.forEach(animal -> < animal.eat(); if (animal instanceof Cat) < ((Cat) animal).meow(); >>); > >

Теперь мы получаем доступ ко всем методам, доступным классу Cat . Посмотрите на журнал, чтобы убедиться, что meow() действительно вызван:

web - 2018-02-16 18:13:45,445 [main] INFO com.baeldung.casting.Cat - cat is eating web - 2018-02-16 18:13:45,454 [main] INFO com.baeldung.casting.Cat - meow web - 2018-02-16 18:13:45,455 [main] INFO com.baeldung.casting.Dog - dog is eating

Обратите внимание, что в приведенном выше примере мы пытаемся опустить только те объекты, которые на самом деле являются экземплярами Cat . Для этого мы используем оператор instanceof .

4.1. Оператор instanceof

Мы часто используем оператор instanceof перед понижением, чтобы проверить, принадлежит ли объект определенному типу:

4.2. Исключение ClassCastException

Если бы мы не проверили тип с помощью оператора instanceof , компилятор не жаловался бы. Но во время выполнения будет исключение.

Чтобы продемонстрировать это, давайте удалим оператор instanceof из приведенного выше кода:

public void uncheckedFeed(List animals) < animals.forEach(animal ->< animal.eat(); ((Cat) animal).meow(); >); >

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

ява,ланг.ClassCastException: com.baeldung.casting.Собака не может быть брошена в com.baeldung.casting.Cat

Это означает, что мы пытаемся преобразовать объект, который является экземпляром Dog , в экземпляр Cat .

ClassCastException’ s всегда выбрасывается во время выполнения, если тип, к которому мы относимся, не соответствует типу реального объекта.

Обратите внимание, что если мы попытаемся перейти к несвязанному типу, компилятор этого не допустит:

Animal animal; String s = (String) animal;

Компилятор говорит: “Не может привести от животного к строке”.

Для компиляции кода оба типа должны находиться в одном дереве наследования.

  • Понижение необходимо для получения доступа к членам, специфичным для подкласса
  • Даункастинг выполняется с помощью оператора cast
  • Чтобы безопасно опустить объект, нам нужен оператор instanceof
  • Если реальный объект не соответствует типу, к которому мы относимся, во время выполнения будет выдано исключение ClassCastException

5. метод cast()

Есть еще один способ приведения объектов с помощью методов Class :

public void whenDowncastToCatWithCastMethod_thenMeowIsCalled() < Animal animal = new Cat(); if (Cat.class.isInstance(animal)) < Cat cat = Cat.class.cast(animal); cat.meow(); >>

В приведенном выше примере вместо операторов cast и instanceof используются методы cast ( ) и isInstance () соответственно.

Обычно используются методы cast() и isInstance() с универсальными типами.

Давайте создадим AnimalFeederGeneric класс с feed() методом, который “кормит” только один тип животных – кошек или собак, в зависимости от значения параметра типа:

public class AnimalFeederGeneric  < private Classtype; public AnimalFeederGeneric(Class type) < this.type = type; >public List feed(List animals) < Listlist = new ArrayList(); animals.forEach(animal -> < if (type.isInstance(animal)) < T objAsType = type.cast(animal); list.add(objAsType); >>); return list; > >

Метод feed() проверяет каждое животное и возвращает только те, которые являются экземплярами T .

Обратите внимание, что экземпляр Class также должен быть передан в универсальный класс, поскольку мы не можем получить его из параметра типа T . В нашем примере мы передаем его в конструктор.

Давайте сделаем T равным Cat и убедимся, что метод возвращает только кошек:

@Test public void whenParameterCat_thenOnlyCatsFed() < Listanimals = new ArrayList<>(); animals.add(new Cat()); animals.add(new Dog()); AnimalFeederGeneric catFeeder = new AnimalFeederGeneric(Cat.class); List fedAnimals = catFeeder.feed(animals); assertTrue(fedAnimals.size() == 1); assertTrue(fedAnimals.get(0) instanceof Cat); >

6. Заключение

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

Как всегда, код для этой статьи доступен на GitHub .

Читайте ещё по теме:

Источник

Приведение типов объектов в Java

Система типов Java состоит из двух типов типов: примитивы и ссылки.

Мы рассмотрели примитивные преобразования вthis article, и здесь мы сосредоточимся на приведении ссылок, чтобы получить хорошее представление о том, как Java обрабатывает типы.

Дальнейшее чтение:

Основы Java Generics

Краткое введение в основы Java Generics.

Java экземпляр оператора

Узнайте об операторе instanceof в Java

2. Примитив против Ссылка

Хотя примитивные преобразования и приведение ссылочных переменных могут выглядеть одинаково, они довольноdifferent concepts.

В обоих случаях мы «превращаем» один тип в другой. Но в упрощенном виде примитивная переменная содержит свое значение, а преобразование примитивной переменной означает необратимые изменения ее значения:

double myDouble = 1.1; int myInt = (int) myDouble; assertNotEquals(myDouble, myInt);

После преобразования в приведенном выше примере переменнаяmyInt равна1, и мы не можем восстановить из нее предыдущее значение1.1.

Reference variables are different; ссылочная переменная относится только к объекту, но не содержит самого объекта.

Приведение ссылочной переменной не затрагивает объект, к которому она относится, а только помечает этот объект другим способом, расширяя или сужая возможности для работы с ним. Upcasting narrows the list of methods and properties available to this object, and downcasting can extend it.с

Ссылка похожа на дистанционное управление объектом. Пульт дистанционного управления имеет больше или меньше кнопок в зависимости от его типа, а сам объект хранится в куче. Когда мы выполняем кастинг, мы меняем тип пульта дистанционного управления, но не меняем сам объект.

3. Апкастинг

Casting from a subclass to a superclass is called upcasting. Как правило, преобразование выполняется неявным образом.

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

Чтобы продемонстрировать апкастинг, давайте определим классAnimal:

Источник

Читайте также:  Древовидная структура в html
Оцените статью