Kodomo

Пользователь

Учебная страница курса биоинформатики,
год поступления 2010

Title

Интерфейсы

В прошлый раз мы выяснили как работает наследование. Основная его функция — перенесение общих для некоторых объектов функций в надкласс во избежание дублирования кода. Часто бывает другая ситуация: разные объекты должны делать разные вещи в одной и той же ситуации. Например разные компьютеры (программы) могут совершенно по разному реагировать на нажатие одних и тех же клавиш на клавиатуре. Или у всех живых существ, имеющих ноги, есть реакция на то что вы им ее отдавите, но реакция разная: взбалмашная дама влепит вам пощечину, собака укусит, а ребенок заплачет.

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

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

   1 public interface Refinable {
   2         void refine();
   3         String getName();
   4 }

А у боса метод для улучшения техники и сотрудников:

   1 void refineEmployee(Refinable r){
   2         speak(r.getName()+", you should work better!");
   3         r.refine();
   4 }

Осталось только понять как сделать чтобы например секретарша была «улучшаема», т.е. реализовывала интерфейс Refinable. Это делается так:

   1 public class Secretary extends Human implements Refinable
   2 {
   3         public void refine() {
   4                 nails_length *= 0.66;
   5                 speak("Yes, Sir! I cut my nails! I`ll work more and more!");
   6         }
   7         public String getName()
   8         {
   9                 return name;
  10         }
  11         .....
  12 }

А чтобы все это работало в нашем коде напишем следующие в OfficeLife:

   1 if(day_num % 7 == 0)
   2         if(secretary != null)
   3                 boss.refineEmployee(secretary);

Здесь возникает один вопрос: переменная secretary имеет тип Secretary, однако мы передаем ее методу refineEmployee которые требует в качестве параметров тип Refinable. На самом деле в Java можно передавать в качестве параметра любые классы реализующие интерфейс указанный в качестве типа параметра.

Что такое в результате интнерфейс — это почти что класс, только в нем не написано что именно делают его методы. А классы реализующие данный интерфейс определяют все его методы.

Инкапсуляция. Модификаторы доступа

Инкапсуляция — это принцип, согласно которому любой класс должен рассматриваться как чёрный ящик — пользователь класса должен видеть и использовать только интерфейсную часть класса (т. е. список декларируемых свойств и методов класса) и не вникать в его внутреннюю реализацию. Все, что необходимо знать пользователю (программисту) про работу того или иного метода описывается в его документации. Для осуществления принципа инкапсуляции в Java используются модификаторы доступа, которые определяют правила доступа к переменным и методам классов.

Есть четыре уровня доступа к свойствам и методам класса:

Области видимости полей и методов с разными модификаторами доступа:

Слово

Из того же пакета

Из другого пакета

Из наследника

public

+

+

+

protected

+

-

+

private

-

-

-

<empty>

+

-

-

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

Давайте опять вернемся к нашему примеру про офис. Ясно, что в примере про класс Secretary мы не хотели бы давать возможность пользователю этого класса задать отрицательную длину ногтей или пустое имя. Для этого мы ограничим доступ к полям name и nails_length модификатором private. Но для того, чтобы иметь возможность менять и узнавать значения этих свойств, нам придется сделать по два публичных метода для каждого из свойств: один метод меняет значение, другой – его возвращает. Обычно их называют как set… и get… (например, getName()), т.е. "сеттеры" и "геттеры". И в сеттере мы можем уже отслеживать, на какое значение пользователь хочет изменить длину ногтей. А для имени в данном случае мы вообще не будем делать сеттер — потому что имя задается при создании объекта и менять его после этого незачем.

На всякий случай хочется уточнить, что значит «пользователь хочет изменить»? Конечно, пользователь в данном случае — это программист, который пользуется данным ему классом (или классом, который он сам же и написал). Например, мы или кто-то написал класс Secretary, и теперь мы пишем какой-то другой класс, использующий класс Secretary. И если программист, написавший класс Secretary не скрыл с помощью модификаторов поля имени и длины ногтей, то мы сможем менять их как захотим, что может привести к ошибкам.

Модификатор static

Бывает так, что в классе нужно создать переменную, нужную классу в целом, а не каждому объекту класса. Например, нужно знать, сколько объектов данного класса создано в данный момент. Или надо хранить значение, одинаковое для всех объектов класса – константу (максимальная длина ногтей у секретарши). В этом случае используется модификатор static. Переменная, объявленная как static, не зависит от конкретного объекта. Ключевое слово static привязывает свойства и методы к классу, а не к объекту. Обращение к ней происходит через имя класса. Например:

   1 public class Secretay {
   2         private static int counter = 0;
   3 
   4         public Secretay(){
   5                 counter++;
   6         }
   7         public static int getSecretaryCount() {
   8                 return counter;
   9         }
  10 }

Теперь в программе мы можем узнать, сколько объектов типа Secretary у нас уже создано в программе:

   1 System.out.println("number of secretaries = " + Secretay.getSecretaryCount ());

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

Нестатические поля недоступны в статических методах.

Ключевое слово final

Ключевое слово final означает: