Справочник от Автор24
Поделись лекцией за скидку на Автор24

Наследование и абстракции.Программирование на языке Java

  • ⌛ 2021 год
  • 👀 433 просмотра
  • 📌 380 загрузок
  • 🏢️ ВШЭ БИ
Выбери формат для чтения
Загружаем конспект в формате pdf
Это займет всего пару минут! А пока ты можешь прочитать работу в формате Word 👇
Конспект лекции по дисциплине «Наследование и абстракции.Программирование на языке Java» pdf
Лекция 8 Наследование и абстракции Программирование на языке Java Роман Гуров ВШЭ БИ 2021 Наследование классов Наследование (англ. inheritance) — концепция ООП, согласно которой класс может наследовать данные и функциональность некоторого другого класса, способствуя повторному использованию общего кода Животное Кошка Сибирская_кошка Барханный_кот Собака Робокот_3000 • Суперкласс (англ. super class), родительский класс (англ. parent class), предок, родитель или надкласс — класс, от которого наследуется некоторый другой класс • Подкласс (англ. subclass), производный класс (англ. derived class), дочерний класс (англ. child class), класс потомок, класс наследник — класс, наследуемый от некоторого суперкласса • Базовый класс (англ. base class) — это класс, находящийся на вершине иерархии наследования классов Кошка – суперкласс для Сибирская_кошка Животное – базовый класс в данной иерархии Наследование классов Класс-наследник сохраняет все методы своего родителя Животное Кошка Барханный_кот Можно представлять, что подкласс полностью содержит в себе содержимое (методы и поля) своего родителя При этом, подкласс может переопределить метод суперкласса, изменяя, таким образом, его поведение Наследование в Java Для наследования в Java используется ключевое слово extends class Base { public void based_method() { System.out.println("I'm based!"); } } class Derived extends Base { public void derived_method() { System.out.println("I'm derived!"); Наследоваться можно только от одного класса } } Наследник будет содержать в себе все поля и методы родительского класса public static void main(String[] args) { Base obj1 = new Base(); Derived obj2 = new Derived(); obj1.based_method(); obj2.based_method(); obj2.derived_method(); } Модификаторы доступа при наследовании Модификаторы доступа при наследовании работают по знакомым правилам Приватные поля и методы родителя недоступны из класса-наследника (по сути наследник – другой класс) protected – недоступен снаружи (не считая package), но доступен внутри всех наследников Переопределение методов class Base { public void present() { System.out.println("It's me, Base!"); } } public static void main(String[] args) { Base base = new Base(); Derived derived = new Derived(); base.present(); derived.present(); } class Derived extends Base { @Override public void present() { System.out.println("It's me, Derived!"); } } Для переопределения метода необходимо, чтобы метод наследника удовлетворял требованиям: • • • • Идентичные название и набор аргументов Тот же или более открытый модификатор доступа Видимость базового метода в классе-наследнике Возвращаемый тип совпадает с или является наследником возвращаемого типа базового метода Аннотация @Override проверяет, что метод действительно переопределен Её использование необязательно, но улучшает читаемость кода и позволяет избежать опечаток в сигнатуре переопределяемого метода Конструктор базового класса Создание объекта класса-наследника обязательно требует вызова конструктора базового класса По-умолчанию, вызывается конструктор по-умолчанию (то есть, с пустыми скобками): class Base { Base() { System.out.println("Base default constructor"); } } public static void main(String[] args) { new Derived(); } class Derived extends Base { Derived() { System.out.println("Derived default constructor"); } } Если требуется вызвать конструктор с параметрами, то нужно использовать ключевое слово super: class Base { Base(int value) { System.out.println("Base constructor with value=" + value); } } class Derived extends Base { Derived() { super(10); System.out.println("Derived default constructor"); } } public static void main(String[] args) { new Derived(); } Ещё о super Ключевое слово super может также использоваться для вызова базового метода из переопределённого: class Base { private static final int value = 5; String tell() { return "I'm base with value " + value; } } class Derived extends Base { @Override String tell() { return "I'm derived and my superclass tells that \"" + super.tell() + '"'; } } public class Main { public static void main(String[] args) { System.out.println( (new Derived()).tell() ); } } Вызов tell() без super привел бы к бесконечной рекурсии Класс Object Если при объявлении класса не указать его надкласс, то он будет наследован от стандартного класса Object Таким образом, транзитивно, все классы в Java являются наследниками Object А значит, каждый класс имеет некоторый унаследованный набор основных методов: public class Object { public String toString() { return this.getClass().getName() + "@" + Integer.toHexString(this.hashCode()); } public boolean equals(Object obj) { return this == obj; } public native int hashCode(); // ... } Переопределение методов класса Object Попробуем создать класс ComplexNumber и переопределить для него методы базового класса Object import java.util.Objects; class ComplexNumber { public ComplexNumber(double real, double imaginary) { real_ = real; imaginary_ = imaginary; } @Override public String toString() { return real_ + " + i * " + imaginary_; } @Override public boolean equals(Object other) { if (this == other) return true; if (!(other instanceof ComplexNumber)) return false; ComplexNumber comp = (ComplexNumber) other; return comp.real_ == real_ && comp.imaginary_ == imaginary_; } @Override public int hashCode() { return Objects.hash(real_, imaginary_); } private final double real_; private final double imaginary_; } public static void main(String[] args) { ComplexNumber comp1 = new ComplexNumber(-10.5, 20.4); ComplexNumber comp2 = new ComplexNumber(-10.5, 20.4); System.out.println(comp1); System.out.println(comp1 == comp2); System.out.println(comp1.equals(comp2)); System.out.println(comp1.hashCode() + " " + comp2.hashCode()); } Полиморфизм в Java Полиморфизм – ещё один основополагающий принцип ООП – в общем смысле, означает возможность обращаться с разными объектами одинаковым образом Примером статического полиморфизма является уже знакомое нам понятие – перегрузка методов Статический полиморфизм происходит на этапе компиляции Но куда более интересным является динамический полиморфизм, работающий на этапе выполнения: class Base { public String tell() { return "I'm base"; } } class Derived extends Base { @Override public String tell() { return "I'm derived"; } } public static void main(String[] args) { Base base = new Derived(); System.out.println(base.tell()); } Можно преобразовать объект к своему родителю и использовать его так, будто бы это и есть родитель Полиморфизм в Java class Base { public String tell() { return "I'm base"; } } public static void main(String[] args) { Base base = new Derived(); System.out.println(base.tell()); } class Derived extends Base { @Override public String tell() { return "I'm derived"; } } Можно преобразовать объект к своему родителю и использовать его так, будто бы это и есть родитель Но объект при этом всё так же остаётся объектом класса-наследника Если в наследнике переопределён какой-то метод, то даже после преобразования к родителю будет вызываться его переопределённая версия Для запрета дальнейшего переопределения метода в наследниках, можно обозначить метод как final Полиморфизм в Java В этом и заключается полиморфизм – мы обрабатываем объекты разных типов так, будто бы тип один: class Base { public String tell() { return "I'm base!"; } } class Foo extends Base { public String tell() { return "I'm foo!"; } } class Bar extends Base { public String tell() { return "I'm bar!"; } } class Baz extends Base { public String tell() { return "I'm baz!"; } } public class Main { public static Base[] generateArray(int n) { Base[] result = new Base[n]; for (int i = 0; i < n; ++i) { if (Math.random() < 0.33) { result[i] = new Foo(); } else if (Math.random() < 0.5) { result[i] = new Bar(); } else { result[i] = new Baz(); } } return result; } public static void main(String[] args) { int n = (new Scanner(System.in)).nextInt(); Base[] bases = generateArray(n); for (int i = 0; i < bases.length; ++i) { System.out.println(bases[i].tell()); } } } Абстрактные классы Рассмотрим цепочку наследования из первого слайда: Животное->Кошка->Робокот_3000 Что из себя должны представлять объекты классов Животное и Кошка? На самом деле, ничего! Они являются лишь абстрактными понятиями, которые может и имеют некоторую функциональность и данные, но не могут быть использованы без конкретной реализации в наследнике Поэтому нужен способ запретить инстанциировать такие абстрактные классы, с чем нам помогает ключевое слово abstract abstract class AbstractNamePrinter { public void printName() { System.out.println(name); } protected String name; } Абстрактные классы abstract class AbstractNamePrinter { public void printName() { System.out.println(name); } protected String name; } class NamePrinter extends AbstractNamePrinter { public NamePrinter(String name) { this.name = name; } } class PrettyNamePrinter extends AbstractNamePrinter { public PrettyNamePrinter(String name) { this.name = name; } @Override public void printName() { System.out.println("[{(" + name + ")}]"); } } Может быть и так, что реализация некоторого метода в абстрактном классе бессмысленна, но хочется потребовать, чтобы каждый наследник предоставлял свою реализацию данного метода Такой метод помечается ключевым словом abstract и не имеет тела abstract class AbstractCat { public abstract String meow(); } class MaineCoon extends AbstractCat { @Override public String meow() { return "MEOW"; } } class RoboCat extends AbstractCat { @Override public String meow() { return "M3-0w!!1"; } } Пример иерархии Рассмотрим пример иерархии классов, реализующей геометрические фигуры public class Point { public Point(double x, double y) { this.x = x; this.y = y; } public double getX() { return x; } public double getY() { return y; } @Override public String toString() { return "(" + x + ", " + y + ")"; } private final double x; private final double y; } Пример иерархии public enum Color { BLACK, WHITE, RED, GREEN, BLUE } public class Triangle extends Shape { private final Point a; private final Point b; private final Point c; public Triangle(Point a, Point b, Point c, Color color) { super(color); this.a = a; this.b = b; this.c = c; } public abstract class Shape { public Shape(Color color) { this.color = color; } @Override public double getArea() { return Math.abs((a.getX() - c.getX()) * (b.getY() - c.getY()) - (b.getX() - c.getX()) * (a.getY() - c.getY())) / 2; } public Color getColor() { return color; } public abstract double getArea(); private final Color color; } } public class Circle extends Shape { private final Point center; private final double radius; } public class Square extends Shape { private final Point center; private final double size; public Circle(Point center, double radius, Color color) { super(color); this.center = center; this.radius = radius; } public Square(Point corner, double size, Color color) { super(color); this.center = corner; this.size = size; } public Point getCenter() { return center; } public Point getCenter() { return center;} public double getRadius() { return radius; } public double getSize() { return size; } @Override public double getArea() { return radius * radius * Math.PI; } @Override public double getArea() { return size * size; } } Пример иерархии public static void main(String[] args) { Circle circle = new Circle( new Point(0, 0), 1, Color.BLACK); Triangle triangle = new Triangle( new Point(0, 0), new Point(1, 0), new Point(0, 1), Color.RED); Square square = new Square( new Point(5, 5), 2, Color.BLUE); Shape shape = triangle; Object object = triangle; triangle = (Triangle) object; Shape[] shapes = {circle, triangle, square}; Shape maxShape = findShapeWithMaxArea(shapes); System.out.println("Shape with max area: " + maxShape); } private static Shape findShapeWithMaxArea(Shape[] shapes) { Shape maxShape = null; double maxArea = Double.NEGATIVE_INFINITY; for (Shape shape: shapes) { double area = shape.getArea(); if (area > maxArea) { maxArea = area; maxShape = shape; } } return maxShape; } Интерфейсы Помимо абстрактных классов, существует понятие интерфейса У интерфейса все методы всегда являются абстрактными и публичными Объявляется интерфейс ключевым словом interface вместо class public interface Runnable { void run(); } Поля могут быть только public static final, то есть константы Также, могут быть и static методы Цель интерфейса – определить некий контракт – набор методов, которые обязуется иметь класс, реализующий этот интерфейс Интерфейсы Для указания того, что класс реализует интерфейс, используется ключевое слово implements public interface Runnable { void run(); } class Program implements Runnable { @Override public void run() { System.out.println("The program is running!"); } } Несмотря на все запреты, интерфейс может предоставлять реализацию метода по-умолчанию: interface StupidRunnable { void run(); default void runWithLog() { System.out.println("Started run"); run(); System.out.println("Finished run"); } } Интерфейсы и множественное наследование В отличие от суперклассов, ограничений на количество реализуемых интерфейсов нет public final class StringBuilder extends AbstractStringBuilder implements Serializable, Comparable, CharSequence { // ... } Эта возможность позволяет спокойно жить без возможности наследоваться от нескольких классов Отсутствие множественного наследования спасает нас от проблемы ромбовидного наследования Base Derived1 Derived2 Frankenstein Примеры интерфейсов CharSequence – последовательность символов public interface CharSequence { int length(); char charAt(int var1); default boolean isEmpty() { return this.length() == 0; } CharSequence subSequence(int var1, int var2); } CharSequence реализован такими классами, как String и StringBuilder public static void printEverySecondChar(CharSequence seq) { for (int i = 1; i < seq.length(); i += 2) { System.out.println(seq.charAt(i)); } } Примеры интерфейсов Appendable – нечто, к чему в конец можно дописывать символы public interface Appendable { Appendable append(CharSequence var1); Appendable append(CharSequence var1, int var2, int var3); Appendable append(char var1); } Appendable реализован тем же StringBuilder, да и многими другими классами
«Наследование и абстракции.Программирование на языке Java» 👇
Готовые курсовые работы и рефераты
Купить от 250 ₽
Решение задач от ИИ за 2 минуты
Решить задачу
Помощь с рефератом от нейросети
Написать ИИ

Тебе могут подойти лекции

Смотреть все 588 лекций
Все самое важное и интересное в Telegram

Все сервисы Справочника в твоем телефоне! Просто напиши Боту, что ты ищешь и он быстро найдет нужную статью, лекцию или пособие для тебя!

Перейти в Telegram Bot