Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЛабРаб.doc
Скачиваний:
4
Добавлен:
02.09.2019
Размер:
236.03 Кб
Скачать

Полиморфизм

Примечательно также и то, что в классах у нас есть методы с одинаковыми именами. Например, методы print(), set(...), length().

Метод set имеет разные сигнатуры: в первом классе он получает два вещественных числовых аргумента, а во втором классе окружностей существует две реализации этого метода, у первой три вещественных числовых аргумента, а второй аргумента два, но первый это объект-точка, а второй — вещественное число. Соответсвенно, в момент вызова метода set никаких сложностей с тем, чтобы определить из какого класса должен исполняться метод — не возникнет (в силу разных наборов параметров).

А вот методы print() и length() вообще не имеют аргументов. И когда мы будем вызывать их в приложении к какому-то объекту, например, так:

obj.print();

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

Явление, когда разный программный код связан с одним и тем же именем (в данном примере с именем метода print()) называется — полиморфизмом (одно имя, но много форм).

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

С полиморфизмом вы уже сталкивались на примере перегрузки методов, в ООП контекстом для вызова конкретной реализации метода является уже не только набор аргументов, но и класс того объекта, к которому метод применяется.

Инкапсуляция

Представленный выше пример содержит проблемный момент, связанный с тем, что ограничения на допустимые значения полей объектов никак не будут учитываться пользователем класса окружностей (внешней частью программы, где создаются и используются объекты класса). По смыслу моделируемой задачи поле r не должно получать отрицательных значений (ведь это радиус окружности). Мы учли это, например, в конструкторе, но пользователь класса может выполнять примерно такие действие, недопустимые с точки зрения предметной области задачи:

Circle o2 = new Circle(); o2.r = -17.5; o2.print(); // получим окружность с отрицательным радиусом

Для ограничения подобных действий, а также для сокрытия внутреннего устройства классов от пользователя (в целях безопасности и обеспечения модульнусти) существуют различные уровни доступа к членам класса (определяемые с помощью модификатора, пока нам хорошо знаком только модификатор public):

Модификаторы и зоны доступа

Тело класса

Пакет, содержащий класс

Класс-наследник (подкласс)

Вся остальная часть программы (например, другие пакеты)

public

+

+

+

+

protected

+

+

+

-

default (модификатор не пишется)

+

+

-

-

private

+

-

-

-

Из таблицы следует, что модификатор public предоставляет к полю или методу доступ из любой части программы. Это самый открытый и общедоступный вариант.

Напротив, модификатор private разрешает обращаться напрямую к члену класса только из самого класса. Это самый закрытый вариант.

Соответсвенно, изменив модификатор перед полем r класса окружностей мы можем решить описанную выше проблему:

private double r; // радиус

Теперь попытка обратиться к свойству r из за пределов класса:

o2.r = -17.5;

Будет приводить к ошибке:

        Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - r has private access in main.Circle             at main.Main.main(Main.java:142)         Java Result: 1

Но что, если нам всё-таки потребуется обратиться к этому полю, чтобы изменить или прочитать его значения? Для этого можно добавить в класс методы, которые будут отвечать за изменение или чтение поля r, но при этом иметь более широкий уровень доступа (например, public):

            public double getR() {                 return r;             }                 public void setR(double a) {                 if(a > 0) {                     r = a;                 } else {                     System.out.println("Радиус окружности должен быть положительным");                 }             }

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

Соответсвенно, теперь можно смело выполнять такой код (ошибки в программе не будет):

Circle o2 = new Circle(); o2.setR(-17.5); // тут будет выведено сообщение о недопустимом значении, но значение поля не изменится o2.print(); // получим окружность с тем радиусом, что изначально был задан с клавиатуры

Методы, подобные созданным, назваются «геттеры» (от слова get, получать) и «cеттеры» (от слова set, устанавливать). Они являются обёртками для доступа к полям на чтение и запись, соответвенно.

Инкапсуляция позволяет запрещать и контролировать использование любых членов класса за пределами его тела.

Задачи

  1. Создайте класс треуголников на координатной плоскости, используя в качестве полей объекты-точки. Реализуйте в классе: a) конструктор, позволяющий задавать вершины с клавиатуры; b) метод print() выводящий описание треугольника на экран; c) методы для вычисления периметра и площади треугольника.

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

  3. Инкапсулируйте поля таким образом, чтобы нельзя изменить значение любого из них так, чтобы вершины оказались на одной прямой.

  4. Создайте метод, поворачивающий треугольник вокруг центра тяжести на указанное в аргументе количество градусов.

Абстрактным называется класс, на основе которого не могут создаваться объекты. При этом наследники класса могут быть не абстрактными, на их основе объекты создавать, соответсвенно, можно.

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

Рассмотрим пример абстрактного класса A:

abstract class A {     int p1;     A() {         p1 = 1;     }     void print() {         System.out.println(p1);     } } class B extends A { } public class Main {     public static void main(String[] args) {         A ob1;         // ошибка: ob1 = new A();         B ob2 = new B(); // будет вызван конструктов по умолчанию из A         ob2.print();     } }

Java разрешит описать конструкторы в классе A, но не разрешит ими воспользоваться (потому что запрещено создавать объекты абстрактного класса).

Обратите внимание на то, что объявление переменной ob1 как ссылки, на объект класса A тоже не запрещается.