Абстрактный класс vs. интерфейс

Запечатанные классы и члены классовSealed Classes and Class Members

Классы могут быть объявлены как запечатанные путем помещения ключевого слова перед определением класса.Classes can be declared as sealed by putting the keyword before the class definition. Пример:For example:

Запечатанный класс не может использоваться в качестве базового класса.A sealed class cannot be used as a base class. Поэтому он также не может быть абстрактным классом.For this reason, it cannot also be an abstract class. Запечатанные классы предотвращают наследование.Sealed classes prevent derivation. Поскольку их нельзя использовать в качестве базовых классов, определенная оптимизация во время выполнения позволяет несколько ускорить вызов членов запечатанных классов.Because they can never be used as a base class, some run-time optimizations can make calling sealed class members slightly faster.

Метод, индексатор, свойство или событие для производного класса, переопределяющего виртуальный член базового класса, может объявлять этот член как запечатанный.A method, indexer, property, or event, on a derived class that is overriding a virtual member of the base class can declare that member as sealed. Это делает бесполезным виртуальный аспект члена для каждого последующего производного класса.This negates the virtual aspect of the member for any further derived class. Для этого в объявлении члена класса необходимо перед ключевым словом override поместить ключевое слово .This is accomplished by putting the keyword before the override keyword in the class member declaration. Пример:For example:

Implementation

While abstract classes and interfaces are supposed to be different concepts, the implementations make that statement sometimes untrue. Sometimes, they are not even what you think they are.

In Java, this rule is strongly enforced, while in PHP, interfaces are abstract classes with no method declared.

In Python, abstract classes are more a programming trick you can get from the ABC module and is actually using metaclasses, and therefore classes. And interfaces are more related to duck typing in this language and it’s a mix between conventions and special methods that call descriptors (the __method__ methods).

Overriding of Abstract Methods

In Java, it is mandatory to override abstract methods of the superclass in the subclass. It is because the subclass inherits abstract methods of the superclass.

Since our subclass includes abstract methods, we need to override them.

Note: If the subclass is also declared abstract, it’s not mandatory to override abstract methods.

Example 2: Overriding Abstract Method

When we run the program, the output will be:

Bark bark.
I can eat.

In the above example, we have created an abstract class Animal. The class contains an abstract method and a non-abstract method .

We have inherited a subclass Dog from the superclass Animal. Here, the subclass Dog overrides the abstract method .

We then created an object d1 of Dog. Using the object, we then called and methods.

Абстрактный класс на всякий случай

В некоторых книгах по C++ приводится такой аргумент по использованию абстрактных классов: в начале разработки программы программист не знает, что может понадобится. Поэтому нужно создать абстрактный класс на всякий случай, а потом можно будет легко переопределить его методы под любую задачу. Поэтому абстрактные классы якобы повышают «гибкость» разработки.

Звучит заманчиво, но давайте проверим этот аргумент на реальном примере. Допустим, в программе уже есть рисование нарисовать круга и прямоугольника. Прошло время и ему неожиданно говорят: «Нарисуй еще ромб». А у него, какая досада, нет абстрактного класса «фигура».

Сравним поведение программиста в двух ситуациях:

Ситуация 1. Абстрактный класс есть

Программист напишет класс «Ромб» и функцию Draw:

class Circle: public Shape
{
public: void Draw() { … }
};

1
2
3
4

classCirclepublicShape

{

publicvoidDraw(){…}

};

Ситуация 2. Абстрактного класса нет

Программист напишет класс «Ромб» и функцию Draw:

class Circle
{
public: void Draw() { … }
};

1
2
3
4

classCircle

{

publicvoidDraw(){…}

};

Чем-нибудь упростил ему задачу абстрактный класс? Ничем.

Поэтому писать абстрактный класс «на всякий случай» точно не стоит.

An Abstract Class Example

In an object-oriented drawing application, you can draw circles, rectangles, lines, Bezier curves, and many other graphic objects. These objects all have certain states (for example: position, orientation, line color, fill color) and behaviors (for example: moveTo, rotate, resize, draw) in common. Some of these states and behaviors are the same for all graphic objects (for example: position, fill color, and moveTo). Others require different implementations (for example, resize or draw). All s must be able to draw or resize themselves; they just differ in how they do it. This is a perfect situation for an abstract superclass. You can take advantage of the similarities and declare all the graphic objects to inherit from the same abstract parent object (for example, ) as shown in
the following figure.


Classes Rectangle, Line, Bezier, and Circle Inherit from GraphicObject

First, you declare an abstract class, , to provide member variables and methods that are wholly shared by all subclasses, such as the current position and the method. also declares abstract methods for methods, such as or , that need to be implemented by all subclasses but must be implemented in different ways. The class can look something like this:

abstract class GraphicObject {
    int x, y;
    ...
    void moveTo(int newX, int newY) {
        ...
    }
    abstract void draw();
    abstract void resize();
}

Each nonabstract subclass of , such as and , must provide implementations for the and methods:

class Circle extends GraphicObject {
    void draw() {
        ...
    }
    void resize() {
        ...
    }
}
class Rectangle extends GraphicObject {
    void draw() {
        ...
    }
    void resize() {
        ...
    }
}

Чистые виртуальные функции с определениями

Оказывается, мы можем определить чистые виртуальные функции:

#include
#include

class Animal // это абстрактный родительский класс
{
protected:
std::string m_name;

public:
Animal(std::string name)
: m_name(name)
{
}

std::string getName() { return m_name; }
virtual const char* speak() = 0; // окончание «= 0» означает, что эта функция является чистой виртуальной функцией
};

const char* Animal::speak() // несмотря на то, что вот здесь её определение
{
return «buzz»;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

#include
#include
 

classAnimal// это абстрактный родительский класс

{

protected

std::stringm_name;

public

Animal(std::stringname)

m_name(name)

{

}

std::stringgetName(){returnm_name;}

virtualconstchar*speak()=;// окончание «= 0» означает, что эта функция является чистой виртуальной функцией

};

constchar*Animal::speak()// несмотря на то, что вот здесь её определение

{

return»buzz»;

}

В этом случае speak() по-прежнему считается чистой виртуальной функцией (хотя позже мы её определили), а Animal по-прежнему считается абстрактным родительским классом (и, следовательно, объекты этого класса не могут быть созданы). Любой класс, который наследует класс Animal, должен переопределить метод speak() или он также будет считаться абстрактным классом.

При определении чистой виртуальной функции, её тело (определение) должно быть записано отдельно (не встроено).

Это полезно, когда вы хотите, чтобы дочерние классы имели возможность переопределять виртуальную функцию или оставить её реализацию по умолчанию (которую предоставляет родительский класс). В случае, если дочерний класс доволен реализацией по умолчанию, то он может просто вызвать её напрямую. Например:

#include
#include

class Animal // это абстрактный родительский класс
{
protected:
std::string m_name;

public:
Animal(std::string name)
: m_name(name)
{
}

std::string getName() { return m_name; }
virtual const char* speak() = 0; // обратите внимание, speak() является чистой виртуальной функцией
};

const char* Animal::speak()
{
return «buzz»; // реализация по умолчанию
}

class Dragonfly: public Animal
{

public:
Dragonfly(std::string name)
: Animal(name)
{
}

virtual const char* speak() // этот класс уже не является абстрактным, так как мы переопределили функцию speak()
{
return Animal::speak(); // используется реализация по умолчанию класса Animal
}
};

int main()
{
Dragonfly dfly(«Barbara»);
std::cout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

#include
#include

classAnimal// это абстрактный родительский класс

{

protected

std::stringm_name;

public

Animal(std::stringname)

m_name(name)

{

}

std::stringgetName(){returnm_name;}

virtualconstchar*speak()=;// обратите внимание, speak() является чистой виртуальной функцией

};

constchar*Animal::speak()

{

return»buzz»;// реализация по умолчанию

}

classDragonflypublicAnimal

{

public

Dragonfly(std::stringname)

Animal(name)

{

}

virtualconstchar*speak()// этот класс уже не является абстрактным, так как мы переопределили функцию speak()

{

returnAnimal::speak();// используется реализация по умолчанию класса Animal

}

};

intmain()

{

Dragonfly dfly(«Barbara»);

std::coutdfly.getName()» says «dfly.speak()’\n’;

}

Результат выполнения программы выше:

Хотя это используется редко.

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

Абстрактные классы также могут
содержать абстрактные методы.

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

А собственно реализация таких методов —
уже задача потомков
.

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

Давайте попробуем на практике.
Пусть предполагается, что все потомки
класса User должны иметь
метод increaseRevenue (увеличить доход).

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

Сам класс User не знает, какой именно доход
будет получать наследник — ведь у работника
это зарплата, а у студента — стипендия.

Поэтому каждый потомок будет реализовывать
этот метод по-своему.

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

Таким образом вы, или другой программист, работающий
с вашим кодом, никак не сможете забыть реализовать
нужный метод в потомке.

Итак, давайте попробуем на практике.
Добавим абстрактный метод increaseRevenue
в класс User:

Пусть наш класс Employee
пока останется без изменений.
В этом случае, даже если не создавать объект
класса Employee, а просто запустить код,
в котором определяются наши классы, — PHP выдаст ошибку.

Давайте проверим — запустите приведенный ниже код:

Давайте теперь напишем реализацию
метода increaseRevenue в классе Employee:

Проверим работу нашего класса:

Реализуем метод increaseRevenue
и в классе Student. Только теперь
наш метод будет увеличивать уже стипендию:

Добавьте в ваш класс User такой же абстрактный
метод increaseRevenue. Напишите реализацию
этого метода в классах Employee и Student.

Добавьте также в ваш класс User абстрактный
метод decreaseRevenue (уменьшить зарплату). Напишите реализацию
этого метода в классах Employee и Student.

PHP — What are Abstract Classes and Methods?

Abstract classes and methods are when the parent class has a named method,
but
need its child class(es) to fill out the tasks.

An abstract class is a class that contains at least one abstract method. An
abstract method is a method that is declared, but not implemented in the code.

An abstract class or method is defined with the
keyword:

Syntax

<?php abstract class
ParentClass {  abstract public function someMethod1(); 
abstract public function someMethod2($name, $color);  abstract
public function someMethod3() : string;}?>

When inheriting from an abstract class, the child class method must be
defined with the same name, and the same or a less restricted access modifier.
So, if the abstract method is defined as protected, the child class method must
be defined as either protected or public, but not private. Also, the type and number of
required arguments must be the same. However, the child classes may have optional
arguments in addition.

So, when a child class is inherited from an abstract class, we have the following rules:

  • The child class method must be defined with the same name and it redeclares the parent abstract method
  • The child class method must be defined with the same or a less restricted
    access modifier
  • The number of required arguments must be the same. However, the child class
    may have optional
    arguments in addition

Let’s look at an example:

Example

<?php // Parent classabstract class Car {  public
$name;  public
function __construct($name) {    $this->name = $name;  } 
abstract public function intro() : string; }// Child classesclass
Audi extends Car {  public
function intro() : string {    return «Choose German
quality! I’m an $this->name!»;
  }}class
Volvo extends Car {  public
function intro() : string {    return «Proud to be
Swedish! I’m a $this->name!»;
  }}class
Citroen extends Car {  public
function intro() : string {    return «French
extravagance! I’m a $this->name!»;
  }}// Create objects from the child classes$audi = new
audi(«Audi»);
echo $audi->intro();echo «»;$volvo = new
volvo(«Volvo»);
echo $volvo->intro();echo «»;$citroen = new citroen(«Citroen»);
echo $citroen->intro();?>

Example Explained

The Audi, Volvo, and Citroen classes are inherited from the Car class. This means that the
Audi, Volvo, and Citroen classes can use the public $name
property as well as the public __construct() method from the
Car class because of inheritance.

But, intro() is an abstract method that should be defined in all the child
classes and they should return a string.

Classes, Properties, and Methods

The term “class” refers to all classes, interfaces, and traits.

4.1. Extends and Implements

The and keywords MUST be declared on the same line as
the class name.

The opening brace for the class MUST go on its own line; the closing brace
for the class MUST go on the next line after the body.

Lists of MAY be split across multiple lines, where each
subsequent line is indented once. When doing so, the first item in the list
MUST be on the next line, and there MUST be only one interface per line.

4.2. Properties

Visibility MUST be declared on all properties.

The keyword MUST NOT be used to declare a property.

There MUST NOT be more than one property declared per statement.

Property names SHOULD NOT be prefixed with a single underscore to indicate
protected or private visibility.

A property declaration looks like the following.

4.3. Methods

Visibility MUST be declared on all methods.

Method names SHOULD NOT be prefixed with a single underscore to indicate
protected or private visibility.

Method names MUST NOT be declared with a space after the method name. The
opening brace MUST go on its own line, and the closing brace MUST go on the
next line following the body. There MUST NOT be a space after the opening
parenthesis, and there MUST NOT be a space before the closing parenthesis.

A method declaration looks like the following. Note the placement of
parentheses, commas, spaces, and braces:

4.4. Method Arguments

In the argument list, there MUST NOT be a space before each comma, and there
MUST be one space after each comma.

Method arguments with default values MUST go at the end of the argument
list.

Argument lists MAY be split across multiple lines, where each subsequent line
is indented once. When doing so, the first item in the list MUST be on the
next line, and there MUST be only one argument per line.

When the argument list is split across multiple lines, the closing parenthesis
and opening brace MUST be placed together on their own line with one space
between them.

4.5. , , and

When present, the and declarations MUST precede the
visibility declaration.

When present, the declaration MUST come after the visibility
declaration.

4.6. Method and Function Calls

When making a method or function call, there MUST NOT be a space between the
method or function name and the opening parenthesis, there MUST NOT be a space
after the opening parenthesis, and there MUST NOT be a space before the
closing parenthesis. In the argument list, there MUST NOT be a space before
each comma, and there MUST be one space after each comma.

Argument lists MAY be split across multiple lines, where each subsequent line
is indented once. When doing so, the first item in the list MUST be on the
next line, and there MUST be only one argument per line.

Overview

  • Code MUST follow a “coding style guide” PSR .

  • Code MUST use 4 spaces for indenting, not tabs.

  • There MUST NOT be a hard limit on line length; the soft limit MUST be 120
    characters; lines SHOULD be 80 characters or less.

  • There MUST be one blank line after the declaration, and there
    MUST be one blank line after the block of declarations.

  • Opening braces for classes MUST go on the next line, and closing braces MUST
    go on the next line after the body.

  • Opening braces for methods MUST go on the next line, and closing braces MUST
    go on the next line after the body.

  • Visibility MUST be declared on all properties and methods; and
    MUST be declared before the visibility; MUST be declared
    after the visibility.

  • Control structure keywords MUST have one space after them; method and
    function calls MUST NOT.

  • Opening braces for control structures MUST go on the same line, and closing
    braces MUST go on the next line after the body.

  • Opening parentheses for control structures MUST NOT have a space after them,
    and closing parentheses for control structures MUST NOT have a space before.

Путаница между конструкцией и обобщением

Разберем сначала ситуацию в физическом мире. Допустим, у нас есть табуретка.

Мы можем добавить к табуретке спинку и получить стул.

В этом случае мы можем сказать, что табуретка — это часть конструкции стула. Назовем эту точку зрения «конструктивный подход»

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

Есть другой подход. Мы поможем посмотреть абстрактно и назвать табуретку и стул одним словом «мебель». Мебель — это обобщение. Само понятие «мебель» происходит от латинского слова mobilis — подвижный. Так называются предметы для сидения, лежания и размещения вещей. Назовем эту точку зрения «абстрактный подход».

Различие между понятием «стул» и понятием «мебель» в том, что стул — это физический предмет, который можно пощупать, а мебель — это абстрактное понятие для удобства описания группы предметов. Поэтому когда мы видим вывеску «Мебель», мы понимаем, что именно мы можем купить в этом магазине, но мы не можем купить «мебель», мы можем купить только конкретный предмет мебели: стул, стол, кресло.

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

  • предметы мебели,
  • предметы, на которых сидят
  • предметы из дерева и т.д.

Все эти обобщения правильны и имеют смысл в определенном контексте. Нельзя указать единственно правильное обобщение. Каждый может предложить свой вариант.

Когда мы переходим к программированию, то часто авторы используют такие аналогии для указания наследования «Мебель — Стул», то есть используют абстрактный подход. Но эти понятия не связаны отношением наследования. Правильное наследование нам подсказывает конструктивный подход: «Табуретка — Стул».

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

В этом случае стул унаследует описание табуретки и получит дополнительно методы и свойства спинки. Поэтому производный класс в программировании — это аналог конструктивного подхода в физическом мире.

Мебель — это обобщение, а для обобщения в языке C++ вводится отдельное понятие — абстрактный класс.

Какая же путаница часто бывает в учебниках по C++? Есть популярная аналогия для иллюстрации  классов: «Млекопитающие-Волки». И его приводят как пример наследования классов. Это неверно. Нет такого зверя — «млекопитающее». Термин «млекопитающее» — это обобщение. Для его реализации требуется использовать абстрактный класс.

А для обычного наследования правильно использовать аналогию «Волк — Собака». Здесь все нормально, потому что существуют как экземпляры волков, так и экземпляры собак.

Constructor Injection

We can’t use @Autowired on a constructor of an abstract class.

Spring doesn’t evaluate the @Autowired annotation on a constructor of an abstract class. The subclass should provide the necessary arguments to the super constructor.

Instead, we should use @Autowired on the constructor of the subclass:

public abstract class BallService {

    private RuleRepository ruleRepository;

    public BallService(RuleRepository ruleRepository) {
        this.ruleRepository = ruleRepository;
    }
}
@Component
public class BasketballService extends BallService {

    @Autowired
    public BasketballService(RuleRepository ruleRepository) {
        super(ruleRepository);
    }
}

General

2.2. Files

All PHP files MUST use the Unix LF (linefeed) line ending.

All PHP files MUST end with a single blank line.

The closing tag MUST be omitted from files containing only PHP.

2.3. Lines

There MUST NOT be a hard limit on line length.

The soft limit on line length MUST be 120 characters; automated style checkers
MUST warn but MUST NOT error at the soft limit.

Lines SHOULD NOT be longer than 80 characters; lines longer than that SHOULD
be split into multiple subsequent lines of no more than 80 characters each.

There MUST NOT be trailing whitespace at the end of non-blank lines.

Blank lines MAY be added to improve readability and to indicate related
blocks of code.

There MUST NOT be more than one statement per line.

Why Java Abstraction?

Abstraction is an important concept of object-oriented programming. Abstraction only shows the needed information and all the unnecessary details are kept hidden. This allows us to manage complexity by omitting or hiding details with a simpler, higher-level idea.

A practical example of abstraction can be motorbike brakes. We know what brake does. When we apply the brake, the motorbike will stop. However, the working of the brake is kept hidden from us.

The major advantage of hiding the working of the brake is that now the manufacturer can implement brake differently for different motorbikes, however, what brake does will be the same.

Let’s take an example that helps us to better understand Java abstraction.

Example 3: Java Abstraction

When we run the program, the output will be:

Bark bark
Meows

In the above example, we have created a superclass Animal. The superclass Animal has an abstract method .

The method cannot be implemented inside Animal. It is because every animal makes different sounds. So, all the subclasses of Animal would have different implementation of .

We cannot implement in Animal in such a way that it can be correct for all subclasses of Animal. So, the implementation of in Animal is kept hidden.

In the above example, Dog makes its own implementation of and Cat makes its own implementation of .

Appendix A. Survey

In writing this style guide, the group took a survey of member projects to
determine common practices. The survey is retained herein for posterity.

A.2. Survey Legend

:
The type of indenting. = “Use a tab”, or = “number of spaces”

:
The “soft” line length limit, in characters. = not discernible or no response, means no limit.

:
The “hard” line length limit, in characters. = not discernible or no response, means no limit.

:
How classes are named. = lowercase only, = lowercase with underscore separators, = StudlyCase.

:
Does the opening brace for a class go on the line as the class keyword, or on the line after it?

:
How are class constants named? = Uppercase with underscore separators.

:
Are the , , and keywords spelled as all case, or all case?

:
How are methods named? = , = lowercase with underscore separators.

:
Does the opening brace for a method go on the line as the method name, or on the line?

:
Does the opening brace for a control structure go on the line, or on the line?

:
Is there a space after the control structure keyword?

:
Do control structures always use braces?

:
When using or , does it go on the line as the previous closing brace, or does it go on the line?

:
How many times are and indented from an opening statement?

:
Do function calls have a space after the function name and before the opening parenthesis?

:
In files containing only PHP, is the closing tag required?

:
What type of line ending is used?

:
When declaring a method, does come first, or does the visibility come first?

:
In a control structure expression, is there a space after the opening parenthesis and a space before the closing parenthesis? = , = .

:
Is there a blank line after the opening PHP tag?

:
A summary of what line the opening braces go on for classes, methods, and control structures.

Используйте интерфейсы и абстрактные классы

В большинстве случаев использование интерфейсов вместе с абстрактными классами будет лучшим решением для любого проекта. Например, в JDK есть  — интерфейс, который содержит много методов. Также там есть абстрактный класс , который является костяком для всех методов интерфейса List. Это значит, что любой подкласс может наследовать этот класс и реализовать только необходимые ему методы.

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

Изменения в Java 8 интерфейсах

Начиная с Java 8 появилась возможность писать реализацию методов в интерфейсах. Мы можем создавать методы по умолчанию, а также статические методы в интерфейсах и обеспечить их реализацию. Следите за обновлениями на Javadevblog.com, чтобы не пропустить новые статьи об интерфейсах в Java 8.

Java

  1. Введение
  2. Первая программа
  3. Java Procedure Programming
    1. Типы данных и переменные
    2. Консольный ввод и вывод
    3. Преобразование типов
    4. Операторы
    5. Условные конструкции
    6. Циклы
    7. Методы
    8. Массивы
    9. String
  4. Java Object-Oriented Programming
    1. Концепции ООП
    2. Классы и объекты
    3. Объекты как параметры методов
    4. Пакеты
    5. Модификаторы доступа и инкапсуляция
    6. Наследование
    7. Иерархия наследования и преобразование типов
    8. Перегрузка методов
    9. Абстрактные классы
    10. Интерфейсы
    11. Статические члены и модификатор
  5. Java Mics
    1. Перечисления
    2. Аннотации
    3. Клонирование объектов
    4. Класс
    5. Классы-оболочки
      1. Классы-оболочки
      2. Автоупаковка
      3. Автоупаковка и перегрузка методов
    6. Строки
      1. StringBuffer и StringBuilder
      2. Форматирование строк
    7. Документирование кода
    8. Лямбда-выражения
      1. Введение в лямбда-выражения
      2. Лямбды как параметры и результаты методов
  6. Java Exception Handling
  7. Java Generics
    1. Обобщения
    2. Ограничения обобщений
    3. Наследование и обобщения
  8. Java Collections
    1. Типы коллекций. Интерфейс Collection
    2. Интерфейс Iterator
    3. Интерфейс List и класс ArrayList
    4. Интерфейс ListIterator
    5. Интерфейсы Comparable и Comporator. Сортировка
    6. Интерфейс Queue и класс ArrayDeque
    7. Класс LinkedList
    8. Интерфейс Set и класс HashSet
    9. Интерфейсы SortedSet и NavigableSet. Класс TreeSet
    10. Интерфейс Map и класс HashMap
    11. Интерфейсы SortedMap и NavigableMap. Класс TreeMap
  9. Java Functional Programming
    1. Введение в Stream API
    2. Встроенные функциональные интерфейсы
    3. Создание потока данных
    4. Фильтрация, перебор элементов и отображение
    5. Сортировка
    6. Получение подпотока и объединение потоков
    7. Методы skip() и limit()
    8. Операции сведения
    9. Метод reduce()
    10. Тип Optional
    11. Метод collect()
    12. Группировка
    13. Параллельные потоки
    14. Параллельные операции над массивами
  10. Java Concurrency
    1. Класс Thread
    2. Создание и выполнение потоков
    3. Завершение и прерывание потока
    4. Синхронизация потоков. Оператор synchronized
    5. Взаимодействие потоков. Методы wait и notify
  11. Java Concurrent
    1. Семафоры
    2. Обмен между потоками. Класс Exchanger
    3. Класс Phaser
    4. Блокировки. ReentrantLock
    5. Условия в блокировках
  12. Java Input/Output
    1. Потоки ввода-вывода
    2. Закрытие потоков
    3. Класс File. Работа с файлами и каталогами
    4. FileInputStream и FileOutputStream
    5. ByteArrayInputStream и ByteArrayOutputStream
    6. BufferedInputStreamи BufferedOutputStream
    7. DataOutputStream и DataInputStream
    8. PrintStream и PrintWriter
    9. FileReader и FileWriter
    10. BufferedReader и BufferedWriter
    11. Сериализация объектов
    12. Работа с ZIP-архивами
    13. Класс Console
  13. Java Date and Time

    1. Класс
    2. Класс
    3. Класс
    4. Класс
    5. Класс
    6. Класс
    7. Класс
  14. Java Regex
  15. Java Reflection
  16. Modularity

Абстрактные классы

Последнее обновление: 20.04.2018

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

При определении абстрактных классов используется ключевое слово abstract:

public abstract class Human{

    private String name;
	
	public String getName() { return name; }
}

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

Human h = new Human();

Кроме обычных методов абстрактный класс может содержать абстрактные методы. Такие методы определяются с помощью
ключевого слова и не имеют никакого функционала:

public abstract void display();

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

Зачем нужны абстрактные классы? Допустим, мы делаем программу для обсулживания банковских операций и определяем в ней три класса:
Person, который описывает человека, Employee, который описывает банковского служащего, и класс Client, который представляет клиента банка.
Очевидно, что классы Employee и Client будут производными от класса Person, так как оба класса имеют некоторые общие поля и методы. И так как
все объекты будут представлять либо сотрудника, либо клиента банка, то напрямую мы от класса Person создавать объекты не будем.
Поэтому имеет смысл сделать его абстрактным.

public class Program{
     
	public static void main(String[] args) {
			
		Employee sam = new Employee("Sam", "Leman Brothers");
		sam.display();
		Client bob = new Client("Bob", "Leman Brothers");
		bob.display();
	}
}
abstract class Person {
    
    private String name;
	
    public String getName() { return name; }
   
    public Person(String name){
    
        this.name=name;
    }
 
    public abstract void display();
}

class Employee extends Person{

    private String bank;
    
    public Employee(String name, String company) {
    
        super(name);
        this.bank = company;
    }
    
    public void display(){
        
        System.out.printf("Employee Name: %s \t Bank: %s \n", super.getName(), bank);
    }
}

class Client extends Person
{
    private String bank;
    
    public Client(String name, String company) {
    
        super(name);
        this.bank = company;
    }
    
    public void display(){
        
        System.out.printf("Client Name: %s \t Bank: %s \n", super.getName(), bank);
    }
}

Другим хрестоматийным примером является системы фигур. В реальности не существует геометрической фигуры как таковой. Есть круг, прямоугольник,
квадрат, но просто фигуры нет. Однако же и круг, и прямоугольник имеют что-то общее и являются фигурами:

// абстрактный класс фигуры
abstract class Figure{
    
    float x; // x-координата точки
    float y; // y-координата точки
 
    Figure(float x, float y){
        
        this.x=x;
        this.y=y;
    }
    // абстрактный метод для получения периметра
    public abstract float getPerimeter();
    // абстрактный метод для получения площади
    public abstract float getArea();
}
// производный класс прямоугольника
class Rectangle extends Figure
{
    private float width;
    private float height;
 
    // конструктор с обращением к конструктору класса Figure
    Rectangle(float x, float y, float width, float height){
        
        super(x,y);
        this.width = width;
        this.height = height;
    }
	
    public float getPerimeter(){
        
        return width * 2 + height * 2;
    }
	
    public float getArea(){
        
        return width * height;
    }
}

НазадВперед

A Sample Hierarchy of File Readers

To understand more clearly the functionality that abstract classes bring to the table, let’s look at another example.

4.1. Defining a Base Abstract Class

So, if we wanted to have several types of file readers, we might create an abstract class that encapsulates what’s common to file reading:

public abstract class BaseFileReader {
    
    protected Path filePath;
    
    protected BaseFileReader(Path filePath) {
        this.filePath = filePath;
    }
    
    public Path getFilePath() {
        return filePath;
    }
    
    public List readFile() throws IOException {
        return Files.lines(filePath)
          .map(this::mapFileLine).collect(Collectors.toList());
    }
    
    protected abstract String mapFileLine(String line);
}

Note that we’ve made filePath protected so that the subclasses can access it if needed. More importantly, we’ve left something undone: how to actually parse a line of text from the file’s contents.

Our plan is simple: while our concrete classes don’t each have a special way to store the file path or walk through the file, they will each have a special way to transform each line.

At first sight, BaseFileReader may seem unnecessary. However, it’s the foundation of a clean, easily extendable design. From it, we can easily implement different versions of a file reader that can focus on their unique business logic.

4.2. Defining Subclasses

A natural implementation is probably one that converts a file’s contents to lowercase:

public class LowercaseFileReader extends BaseFileReader {

    public LowercaseFileReader(Path filePath) {
        super(filePath);
    }

    @Override
    public String mapFileLine(String line) {
        return line.toLowerCase();
    }   
}

Or another might be one that converts a file’s contents to uppercase:

public class UppercaseFileReader extends BaseFileReader {

    public UppercaseFileReader(Path filePath) {
        super(filePath);
    }

    @Override
    public String mapFileLine(String line) {
        return line.toUpperCase();
    }
}

As we can see from this simple example, each subclass can focus on its unique behavior without needing to specify other aspects of file reading.

4.3. Using a Subclass

Finally, using a class that inherits from an abstract one is no different than any other concrete class:

@Test
public void givenLowercaseFileReaderInstance_whenCalledreadFile_thenCorrect() throws Exception {
    URL location = getClass().getClassLoader().getResource("files/test.txt")
    Path path = Paths.get(location.toURI());
    BaseFileReader lowercaseFileReader = new LowercaseFileReader(path);
        
    assertThat(lowercaseFileReader.readFile()).isInstanceOf(List.class);
}

For simplicity’s sake, the target file is located under the src/main/resources/files folder. Hence, we used an application class loader for getting the path of the example file. Feel free to check out our tutorial on class loaders in Java.

Ссылка на основную публикацию