Menu
Coddy logo textTech

Абстрактные классы и методы

Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy — урок 32 из 87.

Иногда вам нужно определить класс, который служит чертежом, но никогда не должен создаваться напрямую. Например, общий класс Shape имеет смысл в качестве родительского, но как бы выглядела «фигура» без конкретной формы? Именно здесь на помощь приходят абстрактные классы.

Абстрактный класс объявляется с помощью ключевого слова abstract, и его экземпляр не может быть создан. Он может содержать как обычные методы с реализацией, так и абстрактные методы, которые не имеют тела:

public abstract class Shape {
    protected String color;
    
    public Shape(String color) {
        this.color = color;
    }
    
    // Абстрактный метод - без реализации
    public abstract double getArea();
    
    // Обычный метод - имеет реализацию
    public String getColor() {
        return color;
    }
}

Абстрактные методы объявляют, что должен делать подкласс, но не как. Любой неабстрактный класс, наследующий абстрактный класс, должен предоставить реализации для всех абстрактных методов:

public class Circle extends Shape {
    private double radius;
    
    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }
    
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

Вы по-прежнему можете использовать абстрактные классы в качестве ссылочных типов, обеспечивая полиморфизм:

Shape shape = new Circle("red", 5.0);
System.out.println(shape.getArea());  // Вызывает реализацию Circle

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

challenge icon

Задание

Легко

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

Вы организуете свой код в четырех файлах:

  • Appliance.java: Создайте абстрактный класс, который служит чертежом для всех приборов. У каждого прибора есть поле brand (String) и поле wattage (int). Включите конструктор, который инициализирует оба поля, методы-геттеры для каждого из них и обычный метод getInfo(), который возвращает: [brand] - [wattage]W. Определите абстрактный метод operate(), который должны реализовать подклассы — он представляет собой то, как каждый прибор выполняет свою основную функцию.
  • WashingMachine.java: Создайте конкретный класс, расширяющий Appliance. У стиральных машин есть дополнительное поле capacity (int), представляющее размер загрузки в кг. Используйте super для обработки инициализации родительского класса. Реализуйте метод operate(), чтобы он выводил: [brand] washing machine is washing [capacity]kg of clothes
  • Microwave.java: Создайте еще один конкретный класс, расширяющий Appliance. У микроволновых печей есть поле power (int), представляющее процент уровня мощности. Реализуйте operate(), чтобы он выводил: [brand] microwave is heating at [power]% power
  • Main.java: Соберите все вместе, используя полиморфизм. Вы получите пять входных данных: название бренда, мощность, вместимость стиральной машины, название другого бренда и уровень мощности микроволновой печи. Создайте WashingMachine и Microwave, затем сохраните их в массиве ссылок Appliance. Пройдите циклом по массиву и для каждого прибора выведите информацию о нем с помощью getInfo(), а затем вызовите operate().

Вы получите пять входных данных в следующем порядке: первый бренд (String), первая мощность (int), вместимость в кг (int), второй бренд (String) и уровень мощности (int).

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

Шпаргалка

Абстрактный класс объявляется с использованием ключевого слова abstract и не может быть инстанцирован напрямую. Он служит шаблоном для подклассов:

public abstract class Shape {
    protected String color;
    
    public Shape(String color) {
        this.color = color;
    }
    
    // Абстрактный метод — без реализации
    public abstract double getArea();
    
    // Обычный метод — имеет реализацию
    public String getColor() {
        return color;
    }
}

Абстрактные методы не имеют тела и должны быть реализованы в неабстрактных подклассах:

public class Circle extends Shape {
    private double radius;
    
    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }
    
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

Абстрактные классы могут использоваться в качестве ссылочных типов для полиморфизма:

Shape shape = new Circle("red", 5.0);
System.out.println(shape.getArea());  // Вызывает реализацию класса Circle

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

Попробуйте сами

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Считать входные данные
        String brand1 = scanner.nextLine();
        int wattage1 = scanner.nextInt();
        int capacity = scanner.nextInt();
        scanner.nextLine(); // поглотить символ новой строки
        String brand2 = scanner.nextLine();
        int power = scanner.nextInt();
        
        // TODO: Создать объект WashingMachine с brand1, wattage1 и capacity
        
        // TODO: Создать объект Microwave с brand2, wattage1 (или соответствующей мощностью) и power
        
        // TODO: Создать массив ссылок Appliance и сохранить оба объекта
        
        // TODO: Пройти циклом по массиву и для каждого устройства:
        // - Вывести информацию о нем, используя getInfo()
        // - Вызвать operate()
    }
}
quiz iconПроверьте себя

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

Все уроки раздела Объектно-ориентированное программирование