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

Конфликты имен при комбинировании интерфейсов

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

interface Intrf_1 { void f(); }

interface Intrf_2 { int f(int i); }

interface Intrf_3 { int f(); }

class A { public int f() { return 1; } }

class B implements Intrf_1, Intrf_1

{ public void f() {}

public int f(int i) { return 1; } // перегружен

}

class C extends A implements Intrf_2

{ public int f(int i) { return 1; } // перегружен

}

class D extends A implements Intrf_3

{ public int f() { return 1; }

}

// ошибка методы различаются только возвращаемым типом:

//! class E extends A implements Intrf_1 {}

//! interface Intrf_4 extends Intrf_1, Intrf_3 {} ///:~

Если удалить комментарии с последних двух строк, то возникнет ошибка как следствие смешения понятий переопределения, реализации и перегрузки.

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

Расширение интерфейса

В Java можно, используя наследование, добавлять новые объявления методов в интерфейсы, а так же комбинировать несколько интерфейсов в один новый интерфейс. В обоих случаях может быть получен новый интерфейс , как видно из примера ниже:

// Расширение интерфейса с наследованием.

interface Monster {

void menace();

}

interface DangerousMonster extends Monster {

void destroy();

}

interface Lethal {

void kill();

}

class DragonZilla implements DangerousMonster {

public void menace() {}

public void destroy() {}

}

interface Vampire

extends DangerousMonster, Lethal {

void drinkBlood();

}

class HorrorShow {

static void u(Monster b) { b.menace(); }

static void v(DangerousMonster d) {

d.menace();

d.destroy();

}

public static void main(String[] args) {

DragonZilla if2 = new DragonZilla();

u(if2);

v(if2);

}

} ///:~

DangerousMonster - простое расширение до Monster, создающее новый интерфейс. Который, в свою очередь реализуется в DragonZilla.

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

Группировка констант

Поскольку любое поле помещенное вами в интерфейс автоматически становится static и final, то интерфейс, по сути, удобная штука для создания групп констант, так же, как и enum в C или C++. К примеру:

// Использование интерфейса для создания групп констант.

package c08;

public interface Months {

int

JANUARY = 1, FEBRUARY = 2, MARCH = 3,

APRIL = 4, MAY = 5, JUNE = 6, JULY = 7,

AUGUST = 8, SEPTEMBER = 9, OCTOBER = 10,

NOVEMBER = 11, DECEMBER = 12;

} ///:~

Заметьте, что в стиле Java используются все буквы в верхнем регистре (с подчеркиваниями для разделения слов) для static final "констант".

Теперь Вы можете использовать эти константы снаружи пакета, просто импортируя c08.* или c08.Months, так же, как Вы импортируете другие пакеты и ссылаться на них примерно так Months.JANUARY. Естественно, то, что Вы получите это просто int, поскольку тут нету никакого дополнительного типа безопасности, какой имеет в C++ enum, но эта техника (наиболее часто используемая) может помочь в случае тяжелого программирования в вашей программе.

Если же Вы хотите получить дополнительные безопасные типы, то вам необходимо создать класс на подобии этого[38]:

// Более здоровая система перечисления.

package c08;

public final class Month2 {

private String name;

private Month2(String nm) { name = nm; }

public String toString() { return name; }

public final static Month2

JAN = new Month2("January"),

FEB = new Month2("February"),

MAR = new Month2("March"),

APR = new Month2("April"),

MAY = new Month2("May"),

JUN = new Month2("June"),

JUL = new Month2("July"),

AUG = new Month2("August"),

SEP = new Month2("September"),

OCT = new Month2("October"),

NOV = new Month2("November"),

DEC = new Month2("December");

public final static Month2[] month = {

JAN, JAN, FEB, MAR, APR, MAY, JUN,

JUL, AUG, SEP, OCT, NOV, DEC

};

public static void main(String[] args) {

Month2 m = Month2.JAN;

System.out.println(m);

m = Month2.month[12];

System.out.println(m);

System.out.println(m == Month2.DEC);

System.out.println(m.equals(Month2.DEC));

}

} ///:~

Этот класс называется Month2, поскольку класс с именем Month уже есть в стандартной библиотеке Java. Этот класс final с конструктором private, поэтому никто не может от него наследовать или создать его представление. Представления final static создаются только однажды, в самом классе: JAN, FEB, MAR и т.д. Эти объекты так же используются в массиве month, что позволяет вам получать доступ к именам по их номеру. (Заметьте, что дополнительный месяц JAN в этом массиве осуществляет смещение на единицу, поэтому то декабрь и будет 12-м, а не 11-м.) В main( ) Вы можете видеть безопасный тип: m является объектом Month2, так что он может быть доступен только как Month2. Предыдущий пример Months.java обрабатывал только значения int, так что, месяц представлялся значением типа int, что само по себе не очень безопасно.

Приведенная техника позволяет вам так же использовать == или equals( ) взаимозаменяемо, как показано в конце метода main( ).