Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Java4

.doc
Скачиваний:
9
Добавлен:
05.06.2015
Размер:
139.26 Кб
Скачать

Лабораторная работа №4.

Программирование интерфейса.

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

Продолжительность работы: 4 часа.

Общие сведения

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

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

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

Создадим тип Attributed, который может использоваться для наделения объектов атрибутами посредством закрепления за ними объектов Attr.

Т.к. в Java поддерживается только одиночное наследование, то новый класс может являться непосредственным расширением всего одного класса. Если создавать класс Attributed, от которого порождаются другие классы, то придется закладывать Attributed в основу всей иерархии, или программисты окажутся перед выбором: расширять ли им класс Attributed или какой-нибудь другой полезный класс. И каждый раз, когда создается некое полезное средство вроде Attributed, возникает желание включить его в корневой класс Object. Если бы это разрешалось, то класс Object вскоре разросся бы до невообразимых размеров.

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

Итак, при возникновении необходимости порождения класса от двух классов применяется множественное наследование (multiple inheritance). Но когда требуется порождение классов, которые сами порождены от одного класса, и проявляется механизм «ромбовидного» наследования, то возникают определенные трудности. В разных языках программирования этот вопрос решается по-разному, а в Java, как уже неоднократно говорилось, множественное наследование запрещено вообще. При расширении класса после слова extends можно написать только одно имя суперкласса. С помощью уточнения super можно обратиться только к членам непосредственно суперкласса. И вот когда при порождении надо использовать несколько предков, используется конструкция интерфейса, в итоге содержащего заголовки методов без реализации и константы. Ведь по сути, проблему создает только реализация методов, а не их описание.

Описание интерфейса начинается со слова interface , перед которым может стоять модификатор public , означающий, как и для класса, что интерфейс доступен всюду. Если же модификатора public нет, интерфейс будет виден только в своем пакете. После слова interface записывается имя интерфейса, потом может стоять слово extends и список интерфейсов-предков через запятую.

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

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

После описания в фигурных скобках указываются в любом порядке константы и заголовки методов. Можно сказать, что в интерфейсе все методы абстрактные, но слово abstract писать не надо. Также, константы всегда статические, и слова static и final указывать не нужно. Каждый класс, реализующий интерфейс, должен реализовать все его методы; если же в классе реализуется только некоторая часть методов интерфейса, то класс (в обязательном порядке) объявляется abstract. Методы интерфейса всегда являются открытыми и не статическими, модификатор public указывать не требуется.

Например, есть общий класс овощей Vegetable, от которого можно породить класс томатов Tomato и огурцов Cucumber. Но салат, который тоже может потребоваться описать, должен наследовать свойства обоих видов овощей. Следующей схемой можно описать эту иерархию:

interface Vegetable{ . . . }

interface Tomato extends Vegetable{ . . . }

interface Cucumber extends Vegetable{ . . . } 

interface Salad extends Tomato, Cucumber{ . . . }

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

interface Vegetable{ . . . } 

interface Tomato extends Vegetable { . . . } 

interface Cucumber extends Vegetable { . . . } 

class Salad implements Tomato, Cucumber { . . . }

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

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

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

На примере объявления рассмотрим случай, когда класс или интерфейс является подтипом более чем одного интерфейса:

interface A { }

interface B extends A { }

class C implements A { }

class D extends C implements B { }

Если в родительских интерфейсах присутствуют методы с одинаковыми именами, существует два пути. Если интерфейсы потомки содержат одноименные методы с разным количеством или типом параметров, то интерфейс D будет содержать два перегруженных метода с одинаковыми именами, но разными сигнатурами. Если же сигнатуры в точности совпадают, то D может содержать лишь один метод с данной сигнатурой. Если методы отличаются лишь типом возвращаемого значения, оба интерфейса реализовать нельзя. Если два метода отличаются только типом возбуждаемых исключений, метод нашего класса обязан соответствовать обоим объявлениям с одинаковыми сигнатурами (количеством и типом параметров), но может возбуждать свои исключения. Однако методы в пределах класса не должны отличаться только составом возбуждаемых исключений; следовательно, должна присутствовать всего одна реализация, удовлетворяющая обеим связкам throws.

Пример.

interface A

{

void setup() throws ExeptionOne;

}

interface B

{

void setup();

}

class D implements A, B

{

public void setup()

{

// ...

}

}

В этом случае класс D может содержать единую реализацию, которая соответствует X.setup и Y.setup. Метод может возбуждать меньше исключений, чем объявлено в его суперклассе, поэтому при объявлении Z.setup необязательно указывать, что в методе возбуждается исключение типа ExceptionOne.

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

Итак, сформулируем резюме о применении интерфейсов. Между интерфейсами и абстрактными классами существует два важных отличия:

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

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

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

Диспетчеры компоновки (layout manager) используются для задания положения компонентов на экране. Наиболее распространенными диспетчерами являются:

  • FlowLayout – располагает последовательно компоненты. Компоненты, которые не помещаются в строку, переносятся на следующую строку

  • GridLayout – располагает компоненты в ячейках сетки. Ячейки имеют равные ширины и высоты

  • BorderLayout – располагает компоненты по четырем сторонам света и один компонент в центе. При изменении размера окна компоненты на «севере» и «юге» сохраняют свою высоту, компоненты на «западе» и «востоке» - свою ширину, центральный компонент изменяет свои размеры так, чтобы заполнить оставшуюся центральную область

  • BoxLayout – располагает компоненты последовательно в заданном направлении внутри прямоугольника.

Компоненты размещаются в контейнерах (например, на панелях: класс JPanel). Для их размещения используется методы add(Component c) и add(Component c, Object constraints). Вторая форма метода add добавляет компонент в нужную позицию в соответствии с диспетчером компоновки.

Для размещения кнопки добавляем в контейнер объект JButton: add(new JButton(“Кнопка”));

Для размещения строки ввода добавляем в контейнер объект JTextField: add(new JTextField());

Для того, чтобы задать текст строки ввода, используется метод setText(string s); а для того, чтобы взять данные из строки ввода – метод string getText().

Если необходимо конвертировать число из строкового представления в числовое, то используется метод parseInt() класса Integer:

JTextField field = new JTextField();

. . .

int value = Integer.parseInt(field.getText());

Каждый компонент может обрабатывать события, заместив определенные методы, вызываемые используемой по умолчанию реализацией метода handleEvents класса Component. Этот метод вызывается с объектом класса Event, описывающего все возможные типы событий. Наиболее часто используемые события, например, те, что связаны с мышью и клавиатурой, делегируются другим методам класса Component.

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

mouseEnter вызывается в том случае, когда мышь входит в компонент.

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

mouseMove вызывается при перемещении мыши в области компонента.

mouseDown вызывается при нажатии кнопки мыши.

mouseDrag вызывается при перемещении мыши с нажатой кнопкой.

mouseUp вызывается при отпускании кнопки мыши.

Кроме обработчика событий мыши (MouseListener для низкоуровневых событий), существуют ActionListener для обработки команд (например, нажатие на кнопку: keyDown и keyUp вызываются при каждом нажатии и отпускании клавиши, событие передается методу вместе с кодом нажатой клавиши), и т.д.

В итоге, обработчики событий – это объекты класса, имплементирующие интерфейсы.

Для работы со специальными событиями, например, с обратными вызовами (callback) из компонентов Button, Scrollbar и Menu, замещается метод action. Этот метод вызывается с исходным событием и со вторым параметром, который представляет собой компонент пользовательского интерфейса, создавший это событие. Необходимо проверить этот объект, разобраться, какой из компонентов послал событие, после чего передать управление соответствующему данному компоненту обработчику.

Модель обратных вызовов реализована в новой модели обработки событий Java1.1. При создании GUI-элемента ему сообщается, какой метод или методы он должен вызывать при возникновении в нем определенного события. Поскольку в Java недопустимо оперировать указателями на методы (методы не являются объектами), то для реализации новой модели необходимо определить класс, реализующий некоторый специальный интерфейс. Затем можно передать экземпляр такого класса GUI-элементу, обеспечивая таким образом обратный вызов. Когда наступит ожидаемое событие, GUI-элемент вызовет соответствующий метод объекта, определенного ранее.

Для каждого события существует порождающий его объект, который можно получить с помощью метода getSource(), и каждому событию пакета AWT соответствует определенный идентификатор, который позволяет получить метод getid(). Это значение используется для того, чтобы отличать события различных типов, которые могут описываться одним и тем же классом событий. Например, для класса FocusEvent возможны два типа событий: FocusEvent.FOCUS_GAINED и FocusEvent.FOCUS_LOST. Подклассы событий содержат информацию, связанную с данным типом события. Например, в классе MouseEvent существуют методы getX(), getY() и getClickCount (). Этот класс наследует, в числе прочих, и методы getModifiers() и getWhen().

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

Пример обработки оконных событий.

class MyFrame extends JFrame implements WindowListener

{

MyFrame()

{

Toolkit kit = Toolkit.getDefaultToolkit();

Dimension screenSize = kit.getScreenSize();

// размещение фрейма по центру экрана

Dimension windowSize = new Dimension(250,120);

setSize(windowSize.width , windowSize.height );

setLocation((screenSize.width - windowSize.width) / 2,

(screenSize.height - windowSize.height) / 2);

// добавляем обработчик событий

addWindowListener(this);

}

public void windowActivated(WindowEvent e) {}

public void windowClosed(WindowEvent e) {}

public void windowClosing(WindowEvent e)

{

System.exit(0);

}

public void windowDeactivated(WindowEvent e) {}

public void windowDeiconified(WindowEvent e) {}

public void windowIconified(WindowEvent e) {}

public void windowOpened(WindowEvent e) {}

}

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

Для каждого интерфейса слушателей событий, содержащего несколько методов, в пакете java.awt.event определен простой класс-адаптер, который обеспечивает пустое тело для каждого из методов соответствующего интерфейса. Когда нужен только один или два таких метода, иногда проще получить подкласс класса-адаптера, чем реализовать интерфейс самостоятельно. При получении подкласса адаптера требуется лишь переопределить те методы, которые нужны, а при прямой реализации интерфейса необходимо определить все методы, в том числе и ненужные в данной программе. Заранее определенные классы-адаптеры называются так же, как и интерфейсы, которые они реализуют: MouseAdapter, WindowAdapter, FocusAdapter, KeyAdapter, и т.д.

Пример использования адаптеров (WindowAdapter).

class MyFrame extends JFrame

{

MyFrame()

{

Toolkit kit = Toolkit.getDefaultToolkit();

Dimension screenSize = kit.getScreenSize();

// размещение фрейма по центру экрана

Dimension windowSize = new Dimension(250,120);

setSize(windowSize.width , windowSize.height );

setLocation((screenSize.width - windowSize.width) / 2,

(screenSize.height - windowSize.height) / 2);

// добавляем обработчик событий

addWindowListener(new Terminator());

}

class Terminator extends WindowAdapter

{

public void windowClosing(WindowEvent e)

{

System.exit(0);

}

}

}

Варианты заданий

Вариант 1.

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

Вариант 2.

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

Учесть масштабирование форм.

Вариант 3.

Реализовать простейший блокнот, предусматривающий включение/выключение переноса строк, выбор шрифта для текста, наличие указателя на текущее положение курсора в тексте (номер строки, номер колонки), кнопки скопировать/вставить в буфер.

Вариант 4.

Разработать простейший калькулятор без сохранения приоритета операций, но с памятью. Реализовать поддержку 4 арифметических действий, квадратный корень. Так же должна быть возможность стирания последней набранной цифры, запись текущего значения в память и вызов данных из памяти. Сгруппированные по блокам элементы должны масштабироваться при изменении размеров окна.

Вариант 5.

Разработать программу векторного рисования. Программа должна позволять пользователю рисовать следующие примитивы: линия, прямоугольник, эллипс. Предусмотреть выбор цветов для рисования (3-4 цвета).

Вариант 6.

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

Вариант 7.

Создать программу, реализующую так называемый «шар с ответами». При вводе вопроса пользователем, программа делает по таймеру 3-х секундную «театральную» паузу и выдает один из 10 случайных ответов (например, «да», «нет», «однозначно да», «будущее неясно» и т.п.)

Вариант 8.

Реализовать программу-будильник с таймером. В таймере необходимо, как в классическом секундомере, предусмотреть кнопки паузы, старта и сброса. Сигнал будильника может быть открытием определенных форм с сообщениями о наступлении контрольного времени, а не звуковым.

ПРИЛОЖЕНИЕ1. Список элементов пакета AWT и событий, которые они порождают. Java 1.0

Элемент

Порождаемое событие

Значение

Button

ActionEvent

Пользователь нажал кнопку

CheckBox

ItemEvent

Пользователь установил или сбросил флажок

CheckBoxMenuItem

ItemEvent

Пользователь установил или сбросил флажок рядом с пунктом меню

Choice

ItemEvent

Пользователь выбрал элемент списка или отменил его выбор

Component

ComponentEvent

Элемент либо перемещен, либо он стал скрытым,либо видимым

 

FocusEvent

Элемент получил или потерял фокус ввода

 

KeyEvent

Пользователь нажал или отпустил клавишу

 

MouseEvent

Пользователь нажал или отпустил кнопку мыши, либо курсор мыши вошел или покинул область, занимаемую элементом, либо пользователь просто переместил мышь или переместил мышь при нажатой кнопке мыши

Container

ContainerEvent

Элемент добавлен в контейнер или удален из него

List

ActionEvent

Пользователь выполнил двойной щелчок мыши на элементе списка

MenuItem

ActionEvent

Пользователь выбрал пункт меню

Scrollbar

AdjustmentEvent

Пользователь осуществил прокрутку

TextComponent

TextEvent

Пользователь внес изменения в текст элемента

TextField

ActionEvent

Пользователь закончил редактирование текста элемента

Window

WindowEvent

Окно было открыто, закрыто, представлено в виде пиктограммы, восстановлено или требует восстановления

Список элементов пакета AWT и событий, которые они порождают. Java1.1

Элемент

Тип события (id)

Смысл события

Тип и значение переменной arg

Button

ACTION_EVENT

Пользователь нажал кнопку

String: обозначение кнопки

Checkbox

ACTION_EVENT

Пользователь активизировал флажок

Boolean: новое состояние флажка

Choice (список выбора)

ACTION_EVENT

Пользователь выбрал элемент списка

String: обозначение выбранного элемента

Element (элемент)

GOT_FOCUS

Получение фокуса ввода

не используется

Element (элемент)

KEY_ACTION

Пользователь нажал функциональную клавишу

не используется, поскольку key содержит константу клавиши

Element (элемент)

KEY_ACTION_ RELEASE

Пользователь отпустил функциональную клавишу

не используется, поскольку key содержит константу клавиши

Element (элемент)

KEY_PRESS

Пользователь нажал клавишу

не используется, поскольку key содержит ASCII-код клавиши

Element (элемент)

KEY_RELEASE

Пользователь отпустил клавишу

не используется, поскольку key содержит ASCII-код клавиши

Element (элемент)

LOST_FOCUS

Потеря фокуса ввода

не используется

Element (элемент)

MOUSE_ENTER>

Курсор мыши попал в область объекта класса Component

не используется

Element (элемент)

MOUSE_EXIT

Курсор мыши вышел из области объекта класса Component

не используется

Element (элемент)

MOUSE_DOWN

Пользователь нажал кнопку мыши

не используется

Element (элемент)

MOUSE_UP

Пользователь отпустил кнопку мыши

не используется

Element (элемент)

MOUSE_MOVE

Пользователь переместил мышь

не используется

Element (элемент)

MOUSE_DRAG

Пользователь переместил мышь при нажатой кнопке мыши

не используется

List (список)

ACTION_EVENT

Пользователь выполнил двойной щелчок мыши на элементе списка

String: обозначение выбранного элемента

List (список)

LIST_SELECT

Пользователь выбрал элемент списка

Integer: индекс выбранного элемента

List (список)

LIST_DESELECT

Пользователь убрал выделение с определенного элемента

Integer: индекс элемента

Menu Item (меню)

ACTION_EVENT

Пользователь выбрал пункт меню

String: обозначение выбранного пункта

Scrollbar (полоса прокрутки)

SCROLL_LINE_UP

Пользователь осуществил прокрутку вверх на строку

Integer: позиция, до которой осуществляется прокрутка

Scrollbar (полоса прокрутки)

SCROLL_LINE_DOWN

Пользователь осуществил прокрутку вниз на строку

Integer: позиция, до которой осуществляется прокрутка

Scrollbar (полоса прокрутки)

SCROLL_PAGE_UP

Пользователь осуществил прокрутку вверх на страницу

Integer: позиция, до которой осуществляется прокрутка

Scrollbar (полоса прокрутки)

SCROLL_PAGE_DOWN

Пользователь осуществил прокрутку вниз на страницу

Integer: позиция, до которой осуществляется прокрутка

Scrollbar (полоса прокрутки)

SCROLL_ABSOLUTE

Пользователь переместил ползунок полосы прокрутки

Integer: позиция, до которой осуществляется прокрутка

Text Field (текст)

ACTION_EVENT

Пользователь ввел текст и нажал [Enter].

String: введенный текст

Window (окно)

WINDOW_DESTROY

Окно закрыто

не используется

Window (окно)

WINDOW_ICONIFY

Окно представлено в виде пиктограммы

не используется

Window (окно)

WINDOW_DEICONIFY

Окно восстановлено

не используется

Window (окно)

WINDOW_MOVED

Окно перемещено

не используется

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]