Правила приведения объектов в Java
Источник:
Medium
В этом руководстве изложены правила приведения объектов при наследовании Java, а также показаны примеры, когда приведение может завершиться ошибкой, и как этого избежать.
Приведение (casting) объектов в наследовании Java — это способ изменить ссылочный тип объекта. Поскольку есть определенные правила и ограничения для приведения объектов в Java, разработчикам важно их понимать во избежании ошибок при выполнении.
Upcasting (неявное приведение, Implicit Casting):
Апкастинг — это приведение из подкласса в суперкласс. Оно всегда разрешено и не требует явного приведения типов.
Parent parent = new Child(); // Upcasting (implicit)
Downcasting (явное, понижающее приведение типов, Explicit Casting)
Даункастинг — это приведение суперкласса к подклассу. Оно требует явного приведения типов и может привести к созданию объекта
ClassCastException во время выполнения, если объект не является экземпляром целевого подкласса.
Parent parent = new Child();
Child child = (Child) parent; // Downcasting (explicit)
Теперь давайте рассмотрим примеры, когда приведение может дать сбой и как это исправить:
Пример 1. Приведение к несвязанному типу (ошибка времени компиляции):
class Parent { }
class Child { }
public class Main {
public static void main(String[] args) {
Parent parent = new Parent();
// Ошибка: Incompatible types (несовместимые типы)
Child child = (Child) parent; // Ошибка времени компиляции
}
}
В чем ошибка: В этом примере нет отношений наследования между классами
Parent и
Child. Попытка приведения от
Parent к
Child не допускается и приводит к ошибке времени компиляции.
Как исправить: Чтобы устранить эту проблему, вам следует либо создать экземпляр класса
Child, либо привести его к общему интерфейсу или суперклассу, совместно используемому обоими классами.
Пример 2. Неправильный даункастинг (ClassCastException во время выполнения):
class Parent { }
class Child extends Parent { }
public class Main {
public static void main(String[] args) {
Parent parent = new Parent();
// ClassCastException при выполнении
Child child = (Child) parent;
}
}
В чем ошибка: В этом примере
Child расширяет
Parent, но объект
parent не является экземпляром
Child. Попытка привести его к
Child становится причиной ошибки
ClassCastException во время выполнения.
Как исправить: Чтобы устранить эту проблему, вам следует проверить тип объекта перед приведением с помощью оператора
instanceof, чтобы избежать возникновения
ClassCastException:
if (parent instanceof Child) {
Child child = (Child) parent;
// Безопасное использование 'child'
} else {
// Обрабатываем случай , когда 'parent' не является экземпляром 'Child'
}
Пример 3. Приведение к подклассу исходного типа (ошибка времени компиляции):
class Parent { }
class Child extends Parent { }
class Grandchild extends Child { }
public class Main {
public static void main(String[] args) {
Child child = new Child();
// Ошибка: несовместимые типы
Grandchild grandchild = (Grandchild) child; // Ошибка времени компиляции
}
}
В чем ошибка: В этом примере
Grandchild — это подкласс
Child, но он не становится тем же самым, что и
Child. Попытка приведения
Child к
Grandchild не допускается и приводит к ошибке времени компиляции.
Как исправить: Чтобы устранить эту проблему, вам следует либо создать экземпляр класса
Grandchild, либо привести его к суперклассу, общему для обоих классов, например,
Parent.
Пример 4: Правильный Downcasting (без ошибок):
class Parent { }
class Child extends Parent { }
public class Main {
public static void main(String[] args) {
Parent parent = new Child();
if (parent instanceof Child) {
Child child = (Child) parent; // Valid downcasting
// Use 'child' safely
}
}
}
В чем ошибка: В этом примере
Child расширяет
Parent, и перед понижающим приведением (даункастингом) мы проверяем, является ли объект
parent экземпляром
Child. Если проверка пройдена, мы выполняем понижающее приведение, которое является безопасным.
Как исправить: всегда используйте оператор
instanceof для проверки типа объекта перед понижением, чтобы избежать ошибок
ClassCastException.
Представленные вашему вниманию примеры иллюстрируют правила и сценарии, связанные с приведением объектов к наследованию Java. Понимание иерархии наследования и использования
instanceof в целях безопасности имеет решающее значение при понижающем приведением (Downcasting).
Зачем нам интерфейс, если у нас есть абстрактный класс?
Источник:
Medium
Благодаря этой публикации вы сможете лучше понять необходимость и варианты применения Abstract Class и Interface в Java.
Вопрос о необходимости абстрактного класса и интерфейса — один из частых вопросов, который встречается на интервью. И на удивление, много людей до сих пор отвечают на него не правильно.
Итак, что же делают Abstract Class и Interface? Давайте представим себе сценарий, когда компания по недвижимости строит дома, но дает нам возможность обустроить в нем кухню по нашему желанию. Реализация будет выглядеть так:
package com.anji.demo;
public interface Kitchen {
public KitchenImpl getKitchen();
}
package com.anji.demo;
public class House implements Kitchen {
@Override
public KitchenImpl getKitchen() {
return new KitchenImpl(KitchenType.OPEN);
}
public void fixDoor() {
}
public void fixTiles() {
}
}
Теперь, вместо того, чтобы записать
Kitchen как интерфейс, давайте представим, что произойдет, если изменить его на абстрактный класс.
package com.anji.demo;
public abstract class Kitchen {
public abstract KitchenImpl getKitchen();
}
package com.anji.demo;
public class House extends Kitchen {
@Override
public KitchenImpl getKitchen() {
return new KitchenImpl(KitchenType.OPEN);
}
public void fixDoor() {
}
public void fixTiles() {
}
}
Пока все идет нормально. Теперь я хочу добавить в свой класс
House какое-то другое поведение, например, иметь солнечную крышу вместо обычной крыши. В этом случае реализация дома выглядит так, как показано ниже.
public class House extends Kitchen, Roof {
@Override
public KitchenImpl getKitchen() {
return new KitchenImpl(KitchenType.OPEN);
}
@Override
public RoofImpl getRoof() {
return new RoofImpl(RoofType.SOLAR);
}
public void fixDoor() {
}
public void fixTiles() {
}
}
Но наша реализация выдает ошибки, поскольку Java не позволяет нам получить несколько вариантов поведения путем расширения нескольких классов.
Язык Java не поддерживает множественное наследование, поскольку это может привести к двусмысленности.
Вы когда-нибудь задумывались, почему в Java есть и класс
Thread, и интерфейс Runnable?
Наша идеальная реализация будет выглядеть вот так:
package com.anji.demo;
public class House implements Kitchen, Roof {
@Override
public KitchenImpl getKitchen() {
return new KitchenImpl(KitchenType.OPEN);
}
@Override
public RoofImpl getRoof() {
return new RoofImpl(RoofType.SOLAR);
}
public void fixDoor() {
}
public void fixTiles() {
}
}
В этом примере мы достигаем множественного наследования в Java с помощью интерфейсов.
Приятного обучения :)
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ