JavaRush /Java блог /Random /Кофе-брейк #257. Правила приведения объектов в Java. Заче...

Кофе-брейк #257. Правила приведения объектов в Java. Зачем нам интерфейс, если у нас есть абстрактный класс?

Статья из группы Random

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

Источник: Medium В этом руководстве изложены правила приведения объектов при наследовании Java, а также показаны примеры, когда приведение может завершиться ошибкой, и как этого избежать. Кофе-брейк #257. Правила приведения объектов в Java. Зачем нам интерфейс, если у нас есть абстрактный класс? - 1Приведение (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 с помощью интерфейсов. Приятного обучения :)
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ