В Java разрешается в одном и том же классе определять два или более метода с одинаковым именем, если только объявления их параметров отличаются. В этом случае методы называются перегружаемыми, а сам процесс – перегрузкой метода (method overloading).
Если у методов одинаковые имена, как Java узнает, какой именно из них вызывается? Ответ прост: перегружаемые методы должны отличаться по типу и/или количеству входных параметров. Даже разного порядка аргументов достаточно для того, чтобы методы считались разными (хотя это не рекомендуется).
Перегрузка по возвращаемым значениям
Логично спросить, почему при перегрузке используются только имена классов и списки аргументов? Почему не идентифицировать методы по их возвращаемым значениям?
Идентифицировать их нельзя, потому что Java в этом случае не может определить, какая версия метода должна выполняться.
При вызове перегружаемого метода для определения нужного варианта в Java используется тип и\или количество аргументов метода. Следовательно, перегружаемые методы должны отличаться по типу и\или количеству их параметров. Возвращаемые типы перегружаемых методов могут отличаться, но самого возвращаемого метода недостаточно, чтобы отличить два разных варианта метода. Когда в исполняющей среде Java встречается вызов перегружаемого метода, в ней просто выполняется тот вариант, параметры которого соответствуют аргументам, указанным в вызове.
Перегрузка методов позволяет поддерживать принцип «один интерфейс, несколько методов».
В языках программирования без перегрузки методов, каждому методу должно быть присвоено однозначное имя. Но зачастую требуется реализовать, по существу, один и тот же метод для разных типов данных.
В таком случае, в языках программирования без перегрузки реализуют несколько методов, которые немного отличаются названиями.
Перегрузка методов ценна тем, что позволяет обращаться к похожим методам по общему имени. Следовательно, имя представляет общее действие, которое должно выполняться. Выбор подходящего варианта метода для конкретной ситуации входит в обязанности компилятора.
Ничто не запрещает вам реализовать несколько перегруженных методов, каждый из которых будет работать совершенно по-разному. Но на практике крайне рекомендуется, чтобы перегруженные методы реализовывали одну и ту же общую операцию.
Перегрузка конструкторов
Наряду с перегрузкой обычных методов можно также выполнять перегрузку конструкторов. Перегружаемые конструкторы – это норма и часто используемый прием.
Соответствующий перегружаемый конструктор вызывается в зависимости от параметров, указываемых при выполнении оператора new.
Если вы пишете для класса несколько конструкторов, иногда бывает удобно вызвать один конструктор из другого, чтобы избежать дублирования кода. Такая операция проводится с использованием ключевого слова this.
В данной лабораторной работе рассматривается принцип инкапсуляции и механизм перегрузки методов.
При выполнении задания продумайте следующие аспекты:
имена полей и типы данных;
как реализовать "уникальность" номера зачетной книжки и номера группы в рамках приложения;
модификаторы доступа для полей и методов;
тип методов, которые требуется реализовать;
если надо, реализуйте приватные методы и добавьте приватные поля, которые должны обеспечивать работу публичных методов.
Создайте публичный класс Student
– студента некоторой специальности некоторого университета. Класс не хранит явным образом информацию о специальности, номере группы\потока, предметах, университете.
В классе должны быть следующие поля:
имя;
фамилия;
год поступления;
уникальный шестизначный номер зачетной книжки.
принимает на вход имя и фамилию. При использовании данного конструктора, в номер зачетной книжки записывается 0;
принимает на вход имя, фамилию, номер зачетной книжки.
метод возвращает имя студента;
метод изменяет имя студента;
метод возвращает фамилию студента;
метод изменяет фамилию студента;
метод возвращает номер зачетной книжки;
метод изменяет номер зачетной книжки;
метод возвращает год поступления;
метод изменяет год поступления.
Создайте публичный класс Group
– студенческой группы. Класс не хранит явным образом специальность и название университета.
В классе должны быть следующие поля:
уникальный номер (в пределах специальности);
массив студентов.
принимает на вход номер группы (в этом случае, количество студентов записывается как 0);
принимает на вход номер группы, количество студентов (инициализация массива, но его элементы остаются пустыми);
принимает на вход массив студентов.
метод возвращает номер группы;
метод изменяет номер группы;
метод возвращает общее число студентов группы;
метод возвращает ссылку на студента по номеру зачетной книжки;
метод удаляет студента из группы по номеру зачетной книжки (помните про корректное удаление элемента из массива);
метод добавляет нового студента в группу (принимает на вход ссылку на объект Student, если массив уже полностью заполнен - реализуйте расширение массива);
метод возвращает массив студентов;
метод возвращает массив студентов, отсортированный по фамилиям (подумайте, как это сделать; если фамилии одинаковы - сортируйте по имени; если имена одинаковы - по номеру зачетной книжки).
В классе Main
напишите код, чтобы протестировать функциональность созданных классов и реализованных методов
Класс Group
содержит внутри себя массив студентов. Методы класса Group
требуют увеличения или уменьшения размера массива, вставки элемента внутри массива, вывод данных массива в отсортированном порядке.
Итого, есть данные и операции над данными (вставка, удаление, изменение размера, вывод в отсортированном порядке).
Используя механизм инкапсуляции и принципы ООП, модифицируйте класс Group
так, чтобы программа больше соответствовала принципам ООП.
Инкапсуляция в Java реализована с помощью использования модификаторов доступа.
Язык Java предоставляет несколько уровней защиты, которые позволяет настраивать область видимости данных и методов. В Java имеется четыре категории видимости элементов класса:
private
– члены класса доступны только членам данного класса. Всё что объявлено private
, доступно только конструкторам и методам внутри класса и нигде больше. Они выполняют служебную или вспомогательную роль в пределах класса и их функциональность не предназначена для внешнего пользования. Закрытие (private
) полей обеспечивает инкапсуляцию;
по умолчанию (package-private) – члены класса доступны классам, которые находятся в этом же пакете;
protected
– члены класса доступны классам, находящимся в том же пакете, и подклассам – в других пакетах;
public
– члены класса доступны для всех классов в этом и других пакетах.
Модификатор класса указывается перед остальной частью описания типа отдельного члена класса. Это означает, что именно с него должен начинаться оператор объявления класса.
Когда член класса обозначается модификатором доступа public
, он становится доступным для любого другого кода в программе, включая и методы, определенные в других классах.
Когда член класса обозначается модификатором private
, он может быть доступен только другим членам этого класса. Следовательно, методы из других классов не имеют доступа к закрытому члену класса.
При отсутствии модификатора доступа, члены класса доступны другим членам класса, который находится в этом же пакете.
Модификатор доступа protected
связан с использованием механизма наследования и будет рассмотрен позже.
Модификатор доступа указывается перед остальной частью описания типа отдельного члена класса (то есть, именно с модификатора доступа начинается объявление члена класса).
Член класса (переменная, конструктор, методы), объявленный public
, доступен из любого метода вне класса.
Всё что объявлено private
, доступно только конструкторам и методам внутри класса и нигде больше. Они выполняют служебную или вспомогательную роль в пределах класса и их функциональность не предназначена для внешнего пользования. Закрытие (private
) полей обеспечивает инкапсуляцию.
В подавляющем большинстве случаев, поля класса объявляются как private
(это не касается статических переменных и констант, там ситуация может быть другая). Должны быть веские основания объявить поле класса общедоступным. Манипулирование данными должно осуществляться только с помощью методов.
Для того чтобы дать возможность получить доступ к переменной или дать возможность изменить ее значение, объявляют специальные методы, которые называются "геттерами" и "сеттерами".
Геттер возвращает значение приватного поля, тогда как сеттер меняет значение приватного поля (новое значение передается в качестве аргумента метода).
Хотя сигнатура и имена геттеров и сеттеров могут быть любыми, приучите себя соблюдать строгий шаблон для объявления геттеров и сеттеров.
Геттер должен иметь префикс get
, после которого идет название поля с большой буквы. Геттер, как правило, не имеет входных аргументов.
Сеттер должен иметь префикс set
, после которого идет название поля с большой буквы. Сеттер принимает на вход новое значение поля. Возвращаемый тип, как правило, void
.
Большинство IDE для Java имеют механизм для генерации геттеров и сеттеров. В IntelliJ IDEA нажмите комбинацию Alt+Insert
находясь в окне редактирования java-файла. Откроется контекстное меню Generate, где вы можете выбрать генерацию геттера и сеттера, после чего указать поля, для которых необходимо сгенерировать методы.
Представим, что нам необходимо создать класс «Корзина» (Cart
), который хранит в себе набор объектов класса «Товар» (Item
).
Какие методы «Корзина» должна предоставлять для внешнего использования? Это могут быть, например, методы «Добавить товар», «Убрать последний добавленный товар», «Подсчет суммы цен товаров в корзине», «Повышение цен в корзине на N процентов» и «Снижение цен в корзине на N процентов».
Как вы можете заметить, это публичные методы, а значит, их можно вызвать через оператор-точку имея ссылку ну объект.
Перечень этих публичных методов и составляет интерфейс класса – то есть, с помощью этих методов объект класса будет взаимодействовать с внешним миром.
Эти методы имеют вполне четко определенные входные аргументы и могут возвращать значения четко определенных типов, и никак иначе. По аналогии с этим, поворот колес автомобиля осуществляется четко определенным образом – поворотом руля, и бензин надо заливать в четко определенное отверстие крышки бензобака, а не как-то еще.
То – как будет реализовано хранение товаров в корзине – это внутренняя логика класса и она не должна быть доступна внешнему миру, она должна быть скрыта от внешнего вмешательства. Другие классы, которые будут использовать объекты класса Cart
не должны знать и не должны иметь доступ к тому – как там «внутри» реализовано хранение товаров, подсчет цен и изменение цены на определенный процент и так далее, они могут только лишь использовать предоставленные им публичные методы. Давайте реализуем «Корзину» с помощью структуры «стек», которая, в свою очередь, реализована обычным массивом.
Как мы видим, массив с товарами, указать на вершину стек объявлены как private
члены класса. Это значит, что мы не можем получить к ним доступ извне – они доступны только внутри данного класса.
Программиста, который будет использовать класс Cart
, не должна волновать ситуация с переполнением стека, с попыткой извлечь элемент из пустого стека, он не должен следить за указателем на вершину стека, он даже не должен знать что это стек.
Для него объект класса Cart
это некоторый объект, который предоставляет «услугу» в виде корзины товаров и с этой корзиной можно работать с помощью определенных публичных методов.
В дальнейшем мы можем переделать класс Cart
и поменять внутреннюю реализацию. Мы можем использовать структуру "очередь", мы можем использовать коллекции, мы можем иначе реализовать операции добавления и удаления элемента в стеке, но если мы сохраним интерфейс класса неизменным, то для внешнего мира эти изменения внутренней логики не будут важны и если мы поменяем внутреннюю логику одного небольшого участка программы, то вся остальная программа будет работать так же.
Название метода
Описание
public Cart(int capacity)
Конструктор с 1 параметром – максимальным количеством товаров в корзине.
public boolean addItem(Item item)
Добавление товара в корзину. Возвращает успешность операции.
public Item deleteLastAddedItem()
Удаление последнего добавленного товара в корзину. Возвращает удаленный товар.
public double calculateItemPrices()
Подсчет суммы цен всех товаров в корзине.
public void raiseItemPrices(double percent)
Поднять цены товаров в корзине на определенный процент (значение процента передается как аргумент метода).
public void cutItemPrices(double percent)
Снизить цены товаров в корзине на определенный процент (значение процента передается как аргумент метода).