Синтаксис и семантика использования внутренних классов и интерфейсов
Причины использования интерфейсов, их реализация. Создание внутреннего класса внутри метода или в случайном контексте и приведение его к базовому типу, доступ к элементам окружающего его класса, ссылка на внешний объект, примеры его использования.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | книга |
Язык | русский |
Дата добавления | 20.11.2009 |
Размер файла | 65,8 K |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
57
Интерфейсы и внутренние классы
Интерфейсы и внутренние классы предоставляют более изощренные пути для организации и контроля над объектами в вашей системе C++, к примеру, не поддерживает данный механизм, но грамотный программист в состоянии сэмулировать его. Тот факт, что этот механизм существует в Java, говорит о том, что он настолько важен, что для него даже созданы специальные ключевые слова. Вы узнали о ключевом слове abstract, которое позволяет вам создавать один или несколько методов в классе, которые не имеют определений, Вы предоставляете только интерфейс, а его реализация будет осуществлена уже в наследниках. Ключевое слово interface создает полностью абстрактный класс, который не предоставляет никаких реализаций ни одного своего компонента. Вы далее узнаете, что interface есть нечто большее, чем просто абстрактный класс, доведенный до конца абстракции, поскольку он позволяет вам создавать вариации C++ множественного наследования, посредством создания класса, который может быть приведен к базовому типу больше раз, чем к один. Во-первых, внутренний класс выглядит, как некий механизм скрытия кода: Вы помещаете его внутри другого класса. Вы так же узнаете, что внутренний класс, не только существует сам по себе, а может соединяться с окружающими классами и такой вид кода будет написан вами более чисто и правильно. Поскольку будет представлена новая концепция кода. Но пройдет некоторое время, пока использование внутренних классов будет достаточно комфортным для вас.
Интерфейсы
Ключевое слово interface осуществляет, на шаг дальше, концепцию, реализованную в abstract. Вы можете думать, что это просто чисто abstract класс. Он позволяет создателю заложить форму (структуру) класса: имена методов, списки аргументов, возвращаемые типы, но только не тела методов. Interface также может содержать поля, но все они будут, хотя и косвенно static и final. Interface предоставляет только форму, образ, но не предоставляет его реализацию.
Interface "говорит": "Все классы, реализующие этот особый интерфейс будут выглядеть одинаково". Поэтому, любой код, использующий interface знает, какой из методов может быть вызван для этого interface, впрочем, это все. Так что interface используется в качестве установления "протокола" между классами. (Некоторые ООЯ имеют даже встроенное ключевое слово protocol, делающее то же самое действие.). Что бы создать interface, используйте ключевое слово interface вместо ключевого слова class. Как и у класса, Вы можете добавить ключевое слово public до interface (но только если этот интерфейс определен в файле с тем же именем) или оставить его пустым, тогда он станет "friendly" и его можно будет использовать только членам одного с ним пакета.Для создания класса согласованного с особенным interface (или группой interface-ов) используйте ключевое слово implements. Тем самым Вы объявляете "Interface это на что похож мой класс, а теперь я скажу, как он должен работать." Все остальное, кроме этого, выглядит, как наследование. Диаграмма для примера с инструментами:
Как только Вы примените interface, то этот класс сразу же становится обычным и в последствии он может быть расширен обычным способом. Вы можете выбрать явно объявления методов в interface как public. Но они таковыми являются, даже если Вы этого и не объявляете. Так что, когда Вы реализуете interface, методы из него должны быть определены как public. В противном случае, они будут по умолчанию friendly и Вы будете ограничены в доступе к ним во время наследования, поскольку доступ будет запрещен компилятором.Это Вы можете увидеть в измененном примере Instrument. Заметьте, что каждый метод в interface строго определен, только так компилятор и позволяет делать. В дополнение, ни один из методов в Instrument не определен как public, но они автоматически public по любому.
Этот кусок кода работает точно так же. Не имеет значения, если Вы приводите к базовому типу, к обычному классу Instrument, abstract классу Instrument, или к интерфейсу Instrument. Поведение остается одно и то же. В частности, Вы можете видеть в методе tune( ), что в нем нет никаких доказательств того, что Instrument это обычный класс или abstract класс или же интерфейс. Это и есть цель: каждый подход (принцип) дает программисту различные варианты контроля над путем создания и использования объектов.
Множественное наследование в Java
Interface это не просто более чистая форма абстрактного класса. Он имее более "высокое" применение. Поскольку interface не имеет реализации всего, что есть в нтем, то нет и массива-хранилища связанного с ним, нет ничего мешающего для комбинации нескольких интерфейсов. И это ценно, поскольку иногда вам требуется нечто: "x есть a и b и c." В C++, этот акт множественных интерфейсов называется множественное наследование, и при этом этот тип тянет за собой "прилипший" багаж, поскольку каждый тип имеет свою реализацию. В Java Вы можете осуществить то же самое, но только один из этих классов может иметь реализацию, так что проблемы, возникающие в C++, не возникают в Java, при комбинировании множества интерфейсов:
В дочерних классах, Вы не можете насильно получить доступ к базовому классу, поскольку он так же абстрактен и монолитен - нерушим (один без абстрактных методов). Если Вы наследуете не от интерфейса, Вы можете наследовать только от него одного. Все остальные из элементов базового класса должны быть интерфейсами. Вы помещаете имена всех интерфейсов после ключевого слова implements и разделяете их при помощи запятых. Вы можете использовать столько интерфейсов, сколько хотите, каждый из них становится независимым типом, к которому Вы в последствии можете привести. Следующий пример демонстрирует комбинирование класса с несколькими интерфейсами для создания нового класса:
В этом примере, как Вы можете видеть, класс Hero комбинирует конкретный класс ActionCharacter с интерфейсами CanFight, CanSwim и CanFly. Когда Вы комбинируете именованный класс с интерфейсами, как в этом примере, этот класс должен быть указан первым, а только затем интерфейсы, в противном случае компилятор выдаст сообщение об ошибке.
Заметьте, что сигнатура fight ( ) та же самая, в интерфейсе CanFight и в классе ActionCharacter, но обратите внимание, что fight( ) не определена в Hero. Правилом для интерфейса является то, что Вы можете наследовать от него, но тогда Вы получите еще один интерфейс. Если же Вы хотите создать объект нового типа, то это должен быть класс со всеми определениями. Даже в силу того, что Hero не предоставляет определения для fight( ), это определение появляется вместе с ActionCharacter. Поскольку оно предоставляется автоматически и есть возможность создать объект от Hero.
В классе Adventure, Вы можете видеть, что в нем есть несколько методов, которые воспринимают в качестве аргументов различные интерфейсы и конкретные классы. Когда объект Hero уже создан, то он может быть передан в качестве параметра в любой их этих методов, что в свою очередь означает, что он может быть приведен к базовому типу к любому из вышеописанных интерфейсов. Поскольку мы рассматриваем путь создания интерфейсов в Java, то на этой дороге программисту не встретится никаких особенных трудностей и не придется специально напрягаться.
Запомните пожалуйста причину использования интерфейсов, кратко ее можно изложить так: возможность приведения к более, чем одному базовому типу. В дополнение вторая причина для использования интерфейсов в том же, в чем и причина использования абстрактных базовых классов: предотвращение создания объектов этого класса программистами и понимания, что это всего лишь интерфейс. Отсюда возникает вопрос: Что Вы должны использовать? Interface или abstract класс? Интерфейс дает вам преимущества абстрактного класса и преимущества интерфейса, так что если нужно создать базовый класс без любых определений методов или переменных, то Вы должны предпочесть абстрактному классу интерфейс. В действительности, если Вы знаете, что что-то собирается стать базовым классом, вашим первым решением должно быть - использовать интерфейс и если только Вы решите, что этот класс должен иметь определения методов и переменных, только тогда Вы должны изменить его на абстрактный класс или если это так необходимо на обычный класс.
Конфликты имен при комбинировании интерфейсов
Вы можете столкнуться с небольшой ловушкой при реализации множественных интерфейсов. В предыдущем примере оба CanFight и ActionCharacter имели идентичные методы void fight ( ). Но это не вызвало проблемы поскольку эти методы одинаковы в обоих классах, но что было бы если бы они были бы разными? Вот пример:
//: c08:InterfaceCollision.java
interface I1 { void f(); }
interface I2 { int f(int i); }
interface I3 { int f(); }
class C { public int f() { return 1; } }
class C2 implements I1, I2 {
public void f() {}
public int f(int i) { return 1; } // перегружен
}
class C3 extends C implements I2 {
public int f(int i) { return 1; } // перегружен
}
class C4 extends C implements I3 {
// Одинаковы, нет проблем:
public int f() { return 1; }
}
// Методы различаются только возвращаемым типом:
//! class C5 extends C implements I1 {}
//! interface I4 extends I1, I3 {} ///:~
Трудность здесь возникает как следствие нехорошего смешения переопределения, реализации и перегрузки, поскольку перегруженные функции могут отличаться только возвращаемым типом. Если раскомментировать последние две линии кода, то возникнет ошибка:
InterfaceCollision.java:23: f() in C cannot
implement f() in I1; attempting to use
incompatible return type
found : int
required: void
InterfaceCollision.java:24: interfaces I3 and I1 are incompatible; both define f
(), but with different return type
Использование одинаковых имен методов в разных интерфейсах предназначенных для комбинирования зачастую так же очень сильно понижает читабельность кода. Старайтесь избегать этого.
Расширение интерфейса с наследованием
Вы можете с легкостью добавлять объявления методов, в интерфейсы, используя наследование, а так же Вы можете комбинировать несколько интерфейсов в один новый интерфес с наследованием. В обоих случаях Вы получите новый интерфейс, как видно из примера ниже:
//: c08:HorrorShow.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++. К примеру:
//: c08:Months.java
// Использование интерфейса для создания групп констант.
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, но эта техника (наиболее часто используемая) может помочь в случае тяжелого программирования в вашей программе.
Этот класс называется Month2, поскольку класс с именем Month уже есть в стандартной библиотеке Java. Этот класс final с конструктором private, поэтому никто не может от него наследовать или создать его представление. Представления final static создаются только однажды, в самом классе: JAN, FEB, MAR и т.д. Эти объекты так же используются в массиве month, что позволяет вам получать доступ к именам по их номеру. (Заметьте, что дополнительный месяц JAN в этом массиве осуществляет смещение на единицу, поэтому то декабрь и будет 12-м, а не 11-м.) В main ( ) Вы можете видеть безопасный тип: m является объектом Month2, так что он может быть доступен только как Month2. Предыдущий пример Months.java обрабатывал только значения int, так что, месяц представлялся значением типа int, что само по себе не очень безопасно.
Приведенная техника позволяет вам так же использовать == или equals ( ) взаимозаменяемо, как показано в конце метода main( ).
Инициализирование полей в интерфейсах
Поля определенные в интерфейсах автоматически становятся static и final. Они не могут быть пустыми (чистыми) final переменными, но они могут быть инициализированы не постоянными выражениями. К примеру:
//: c08:RandVals.java
// Инициализирование полей интерфейса
// не постоянными инициализаторами.
import java.util.*;
public interface RandVals {int rint = (int)(Math.random() * 10);
long rlong = (long)(Math.random() * 10);
float rfloat = (float)(Math.random() * 10);
double rdouble = Math.random() * 10;
} ///:~
Синтаксис внутреннего интерфейса внутри класса очевиден и похож на не внутренние интерфейсы, которые могут быть public или friendly. Вы так же видите, что оба внутренних интерфейса public и friendly могут быть реализованы как public, friendly и private внутренние классы.
С этой новой особенностью интерфейсы могут так же быть private, как видно из A.D (тот же самый синтаксис используется для внутренний интерфейсов и для внутренних классов). Что же хорошего в private внутреннем интерфейсе? Как Вы можете догадаться, он может быть реализован только как private внутренний класс, как, например в DImp, но в A.DImp2 видно, что он так же может быть реализован и как public класс. Но все равно, A.DImp2 может быть использован только как сам. Однако не следует пропустить упоминание о том, что реализация private интерфейса это всего лишь путь для принудительного определения методов в этом интерфейсе, без добавления любой информации о типе (это так и есть, без возможности любого приведения к базовому типу).
Метод getD ( ) вызывает дальнейшие затруднения связанные с private интерфейсом: это public метод, который возвращает ссылку на private интерфейс. И что Вы будете делать с этим возвращенным значением? В main ( ), Вы можете видеть несколько провалившихся попыток что-либо поделать с ним. Единственная вещь способная работать с ним - это может быть объект имеющий на него права, в нашем случае другой объект A, посредством метода received ( ).
Интерфейс E показывает, что интерфейсы могут быть вложены друг в друга. Но все равно, правила насчет интерфейсов следующие, все элементы интерфейса должны быть public, но в случае внутреннего интерфейса внутри другого интерфейса они все и так становятся public автоматически и не могут быть сделаны private.
NestingInterfaces показывает различные пути реализации внутренних интерфейсов. В частности заметьте, что когда Вы реализуете интерфейс, вам не нужно так же реализовывать внутренние интерфейсы, находящиеся в нем. Так же, private интерфейсы не могут быть реализованы снаружи класса, в котором они определены.
Внутренние классы
В Java есть возможность поместить определение одного класса внутри определения другого класса. Такое помещение называется внутренний класс. Внутренний класс позволяет вам группировать классы вместе, которые логично было бы разместить в одном месте и при этом ими легко управлять визуально. Однако важно понять, чем внутренний класс отличается от композиции.
Зачастую, пока Вы узнаете о внутренних классах, Вы задаетесь вопросом о целесообразности их применения, а иногда, что их применение даже вовсе и не нужно. В конце же этой секции, после того, как будет описан весь синтаксис и семантика внутренних классов, Вы найдете несколько примеров, которые прояснят все преимущества от внутренних классов.
Для создания внутреннего класса, как может быть Вы, и ожидали, нужно поместить его описание внутри класса:
//: c08:Parcel1.java
// Создание внутреннего класса.
public class Parcel1 {
class Contents {
private int i = 11;
public int value() { return i; }
}
class Destination {
private String label;
Destination(String whereTo) {
label = whereTo;
}
String readLabel() { return label; }
}
// Использование внутреннего класса
// похоже на использование обычного класса:
public void ship(String dest) {
Contents c = new Contents();
Destination d = new Destination(dest);
System.out.println(d.readLabel());
}
public static void main(String[] args) {
Parcel1 p = new Parcel1();
p.ship("Tanzania");
}
} ///:~
Внутренний класс, использованный внутри ship ( ), выглядит так же, как и любой другой класс. И только одно различие бросается в глаза, это то, что его имя расположено после Parcel1. Но в дальнейшем Вы увидите, что это далеко не единственное различие.
Более типичный случай - внешний класс имеет метод, который возвращает ссылку на внутренний класс, например, так:
//: c08:Parcel2.java
// Возвращение ссылки на внутренний класс.
public class Parcel2 {
class Contents {
private int i = 11;
public int value() { return i; }
}
class Destination {
private String label;
Destination(String whereTo) {
label = whereTo;
}
String readLabel() { return label; }
}
public Destination to(String s) {
return new Destination(s);
}
public Contents cont() {
return new Contents();
}
public void ship(String dest) {
Contents c = cont();
Destination d = to(dest);
System.out.println(d.readLabel());
}
public static void main(String[] args) {
Parcel2 p = new Parcel2();
p.ship("Tanzania");
Parcel2 q = new Parcel2();
// Определение ссылки на внутренний класс:
Parcel2.Contents c = q.cont();
Parcel2.Destination d = q.to("Borneo");
}
} ///:~
Если Вы хотите создать объект внутреннего класса, где либо еще, кроме не статического метода внешнего класса, то Вы должны определить тип этого объекта, как OuterClassName.InnerClassName, как, например, в main( ).
Внутренний класс и приведение к базовому типу
Недавно, Вы узнали о том, что в Java есть достаточно хорошие механизмы для скрытия классов, их достаточно сделать "friendly" и они будут видны только для классов этого же пакета, и не нужно никаких внутренних классов.
Но все равно, внутренние классы действительно проявляются, когда Вы пытаетесь привести к базовому типу и в частности к interface. (Эффект возникновения ссылки на интерфейс от объекта, который реализует его же при попытке апкастинга к базовому классу.) Это происходит потому, что внутренний класс, реализованный на интерфейсе, может быть потом полностью, но невидимо и недоступно для всех приведен к базовому классу, что обычно называется скрытой реализацией. Все что Вы получите назад это просто ссылка на базовый класс или интерфейс.
Сперва общие интерфейсы должны быть определены в их собственных файлах, тогда они могут быть использованы во всех примерах:
//: c08:Destination.java
public interface Destination {
String readLabel();
} ///:~
//: c08:Contents.java
public interface Contents {
int value();
} ///:~
Теперь Contents и Destination представляют интерфейсы доступные для программиста - клиента. (Не забудьте, что interface автоматически делает всех членов класса public.)
Когда Вы получите назад ссылку на базовый класс или на интерфейс, то возможно, что Вы уже не сможете когда либо найти настоящий тип этого объекта, как показано ниже:
//: c08:Parcel3.java
// Возвращение ссылки на внутренний класс.
public class Parcel3 {
private class PContents implements Contents {
private int i = 11;
public int value() { return i; }
}
protected class PDestination
implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
public String readLabel() { return label; }
}
public Destination dest(String s) {
return new PDestination(s);
}
public Contents cont() {
return new PContents();
}
}
class Test {
public static void main(String[] args) {
Parcel3 p = new Parcel3();
Contents c = p.cont();
Destination d = p.dest("Tanzania");
// Незаконно - нельзя получить доступ к private классу:
//! Parcel3.PContents pc = p.new PContents();
}
} ///:~
Заметьте, поскольку main ( ) в Test, то когда Вы захотите запустить эту программу, Вы не выполните Parcel3, а вместо этого: java Test
В примере, main( ) должен быть расположен в отдельном классе, для того, что бы продемонстрировать защищенность внутреннего класса PContents.
В Parcel3 было добавлено что-то новое: внутренний класс PContents - private, так что никто кроме Parcel3 не может получить к нему доступ. PDestination является protected, так что никто кроме Parcel3 и классов из пакета Parcel3 (поскольку protected так же дает доступ к членам пакета, protected так же означает "friendly"), и наследников Parcel3 не смогут получить доступ к PDestination. Это означает, что клиентский программист имеет ограниченные знания об этих объектах и ограниченный доступ к ним. В действительности, Вы никогда не сможете привести к дочернему типу private внутренний класс (или к protected внутреннему классу, даже если Вы являетесь наследующим), и это происходит потому, что Вы не можете получить доступ к имени, как это можно посмотреть в классе Test. Поэтому private внутренний класс предоставляет разработчику возможность полностью исключить изменение его кода и полностью скрыть детали его реализации. В дополнение, расширение интерфейса так же не принесет пользы, поскольку клиент программист не сможет получить доступ ни к одному из методов, поскольку они не являются частью public interface класса. При этом так же имеется возможность для компилятора по созданию более эффективного кода.
Нормальный (не внутренний) класс не может быть сделан private или protected, только как public или friendly.
Внутренние классы в методе и контексте
То, что Вы видели в предыдущем разделе - типичное использование для внутренних классов. В основном же, тот код, который Вы будете писать и читать, используя внутренние классы должен быть ясным и простым. Эти классы должны быть легки для понимания. Тем не менее, проектировка внутренних классов вами вполне доведена до конца, но существует еще и некоторое число других, более запутанных способов создания внутренних классов. Оные Вы можете использовать по собственному желанию: внутренний класс может быть создан внутри метода или даже в случайном контексте. Вот две причины побуждающие делать это:
1. Как было показано предварительно, Вы реализуете интерфейс какого-то типа, так что Вы можете создать и вернуть ссылку.
2. Вы решаете какую-то проблему и хотите создать класс, который эту проблему исправляет, но Вы не хотите, что бы этот класс был доступен для кого-то еще.
В следующих примерах, предыдущий код будет изменен для получения следующих "результатов":
1. Класс определен в методе
2. Класс определен в контексте внутри метода
3. Анонимный класс реализует интерфейс
4. Анонимный класс расширяет класс, который имеет конструктор не по умолчанию
5. Анонимный класс, осуществляющий инициализацию полей
6. Анонимный класс, который осуществляет создание, используя инициализацию экземпляра (анонимный внутренний класс не может быть с конструктором)
Хотя это и обычный класс с реализацией, но Wrapping так же используется и в качестве общего интерфейса к его производным классам:
//: c08:Wrapping.java
public class Wrapping {
private int i;
public Wrapping(int x) { i = x; }
public int value() { return i; }
} ///:~
Вы должны знать, что у Wrapping есть конструктор, требующий аргумента. Это сделано для того, что бы было немножко поинтереснее.
В первом примере показывается создание целого класса внутри контекста метода (вместо контекста другого класса):
//: c08:Parcel4.java
// Вложенность класса внутри метода.
public class Parcel4 {
public Destination dest(String s) {
class PDestination
implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
public String readLabel() { return label; }
}
return new PDestination(s);
}
public static void main(String[] args) {
Parcel4 p = new Parcel4();
Destination d = p.dest("Tanzania");
}
} ///:~
Класс PDestination скорее часть dest( ) чем Parcel4. (Так же заметьте, что Вы можете использовать идентификатор класса PDestination для внутреннего класса внутри каждого класса в одной и той же поддиректории без конфликта имен.) Следовательно, PDestination не может быть доступен снаружи dest( ). Заметьте, что приведение к базовому типу происходит в операторе возврата, ничего не попадает наружу из dest( ), кроме ссылки на Destination, т.е. на базовый класс. Естественно, факт того, что имя класса PDestination помещено внутри dest( ) еще не означает, что PDestination не правильный объект, который возвращает dest( ).
Следующий пример покажет вам, как Вы можете вложить внутренний класс внутри любого случайного контекста:
//: c08:Parcel5.java
// Вложенный класс внутри контекста.
public class Parcel5 {
private void internalTracking(boolean b) {
if(b) {
class TrackingSlip {
private String id;
TrackingSlip(String s) {
id = s;
}
String getSlip() { return id; }
}
TrackingSlip ts = new TrackingSlip("slip");
String s = ts.getSlip();
}
// Нельзя его здесь использовать! Вне контекста:
//! TrackingSlip ts = new TrackingSlip("x");
}
public void track() { internalTracking(true); }
public static void main(String[] args) {
Parcel5 p = new Parcel5();
p.track();
}
} ///:~
Класс TrackingSlip помещен внутри контекста, а так же внутри оператора if. Но это не означает, что этот класс условно создается, он будет скомпилирован вместе с остальным кодом. Тем не менее он не будет доступен снаружи контекста, в котором он был объявлен. Кроме этой особенности он выглядит точно так же, как обычный класс.
Анонимный внутренний класс
Следующий пример несколько странен:
//: c08:Parcel6.java
// Метод возвращающий анонимный внутренний класс.
public class Parcel6 {
public Contents cont() {
return new Contents() {
private int i = 11;
public int value() { return i; }
}; // В этом случае требуется точка с запятой
}
public static void main(String[] args) {
Parcel6 p = new Parcel6();
Contents c = p.cont();
}
} ///:~
Метод cont( ) комбинирует создание возвращаемого значения с описанием класса, который и есть это возвращаемое значение! В дополнение этот класс еще и не имеет своего имени. Делая тему обсуждения немного запутанной, он выглядит как будто Вы начинаете создавать объект Contents:
return new Contents()
Но затем, до того, как Вы поставите точку запятую, Вы заявляете: "Но подождите, я думаю, я описался в определении класса":
return new Contents() {
private int i = 11;
public int value() { return i; }
};
А вот, что означает этот синтаксис: "Создание объекта анонимного класса, который наследует от Contents." Ссылка, возвращаемая выражением new, автоматически приводится к базовому типу, к ссылке Contents. Синтаксис анонимного внутреннего класса - короткая запись следующего кода:
class MyContents implements Contents {
private int i = 11;
public int value() { return i; }
}
return new MyContents();
В анонимном внутреннем классе, Contents создается при помощи конструктора по умолчанию. Следующий же код показывает, как поступить, если нужно создать его с помощью конструктора с аргументами:
//: c08:Parcel7.java
// Анонимный внутренний класс вызывающий
// конструткор базового класса.
public class Parcel7 {
public Wrapping wrap(int x) {
// Вызов базового конструктора:
return new Wrapping(x) {
public int value() {
return super.value() * 47;
}
}; // Требуется точка с запятой
}
public static void main(String[] args) {
Parcel7 p = new Parcel7();
Wrapping w = p.wrap(10);
}
} ///:~
То есть, Вы просто передаете соответствующий аргумент в конструктор базового класса, конкретно здесь x передается в new Wrapping(x). Анонимный класс не может иметь конструктор, в котором Вы могли бы нормально вызвать super( ).
В обоих предыдущих примерах, точка с запятой не означает конец тела класса (как в C++). Вместо этого, они показывают конец выражения, которое содержит анонимный класс. Таким образом, это равносильно использованию точки запятой где нибудь еще (т.е. они имеют то же значение, что и в любом другом месте).
Что случиться, если вам потребуется осуществить некую часть инициализации для объекта внутреннего анонимного класса? Поскольку он анонимный, то у него нет имени, которое можно передать конструктору, поэтому у него не может быть конструктора. Но все равно, Вы можете осуществить инициализацию в точке определения ваших полей:
//: c08:Parcel8.java
// Анонимный внутренний класс осуществляющий
// инициализацию. Краткая версия
// Parcel5.java.
public class Parcel8 {
// Аргумент должен быть final для использования внутри
// анонимного внутреннего класса:
public Destination dest(final String dest) {
return new Destination() {
private String label = dest;
public String readLabel() { return label; }
};
}
public static void main(String[] args) {
Parcel8 p = new Parcel8();
Destination d = p.dest("Tanzania");
}
} ///:~
Если Вы определяете анонимный внутренний класс и хотите использовать в нем объект, который определен снаружи этого анонимного внутреннего класса, то тогда компилятор потребует, что бы данный объект был объявлен, как final. Вот поэтому-то аргумент dest( ) - final. Если же Вы забыли, в противном случае появляется ошибка времени выполнения.
Как только Вы научились получать доступ к объектам вне внутреннего класса, так сразу же возникает вопрос, а что делать, если нужно осуществить некое действие похожее на конструктор При помощи инициализации экземпляра, Вы можете, в действительности, создать конструткор для анонимного внутреннего класса:
//: c08:Parcel9.java
// Использование "инициализации экземпляра" для осуществления
// создания анонимного внутреннего класса.
public class Parcel9 {
public Destination
dest(final String dest, final float price) {
return new Destination() {
private int cost;
// Экземплярная инициализация для каждого объекта:
{
cost = Math.round(price);
if(cost > 100)
System.out.println("Over budget!");
}
private String label = dest;
public String readLabel() { return label; }
};
}
public static void main(String[] args) {
Parcel9 p = new Parcel9();
Destination d = p.dest("Tanzania", 101.395F);
}
} ///:~
Внутри инициализатора Вы можете видеть код, который не будет исполнен как часть инициализатора полей (т.е., выражение if). На самом же деле, инициализатор экземпляра по своей сути есть ничто иное, чем конструктор анонимного класса. Разумеется, что он несколько ограничен; Вы не можете перегрузить инциализатор экземпляра, поэтому у вас есть только один такой "конструктор".
Связь с внешним классом
Поскольку, внутренний класс предоставлялся только для целей скрытия имени и кода, что несомненно помогает, но все же не является всеобъемлющей особенностью внутренних классов. Тем не менее, имеется еще один способ использования внутренних классов. Когда Вы создаете внутренний класс, объект этого внутреннего класса имеет связь с окружающим его объектом и поэтому он имеет доступ к элементам этого объекта, без каких либо специальных предикатов. В дополнение внутренние классы имеют права доступа ко всем элементам окружающего его класса[40]. Нижеследующий пример как раз это и показывает:
//: c08:Sequence.java
// Поддержка последовательности объектов.
interface Selector {
boolean end();
Object current();
void next();
}
public class Sequence {
private Object[] obs;
private int next = 0;
public Sequence(int size) {
obs = new Object[size];
}
public void add(Object x) {
if(next < obs.length) {
obs[next] = x;
next++;
}
}
private class SSelector implements Selector {
int i = 0;
public boolean end() {
return i == obs.length;
}
public Object current() {
return obs[i];
}
public void next() {
if(i < obs.length) i++;
}
}
public Selector getSelector() {
return new SSelector();
}
public static void main(String[] args) {
Sequence s = new Sequence(10);
for(int i = 0; i < 10; i++)
s.add(Integer.toString(i));
Selector sl = s.getSelector();
while(!sl.end()) {
System.out.println(sl.current());
sl.next();
}
}
} ///:~
Sequence - просто массив с фиксированным размером, с элементами типа Object, с классом обернутым вокруг него. Вы вызываете метод add( ) для добавления нового Object-а в конец последовательности (если там еще есть место). Для выборки каждого из объекта в Sequence, имеется интерфейс Selector, который позволяет так же вам узнать, что вы в конце end( ), для просмотра текущего current( ) Object-а, и для перехода на следующий next( ) Object в последовательности Sequence. Поскольку Selector - interface, многие другие классы могут реализовать его по своему собственному усмотрению, а так же многие методы могут получать этот интерфейс как аргумент, в порядке создания наследуемого кода.
Здесь, SSelector - private класс, который предоставляет функциональность Selector. В main( ), Вы можете видеть создание Sequence, следующее за добавлением объектов String. Затем Selector создается путем вызова getSelector( ) и при его же помощи можно перемещаться по Sequence выбирая каждый из элементов.
На первый взгляд, создание SSelector выглядит, как просто создание другого внутреннего класса. Но просмотрите его более внимательно. Заметьте, что каждый из методов end( ), current( ) и next( ) ссылаются на obs, который является ссылкой и не является частью SSelector, но он заменяет private поле в окружающем классе. Тем не менее, внутренний класс может получить доступ к методам и полям окружающего класса, как если бы они были его собственными полями.
Так что внутренний класс имеет автоматически доступ к элементам окружающего его класса. А как же тогда такое происходит? Внутренний класс должен хранить ссылку на объект окружающего класса, ответственный за его создание. Потом, когда Вы будете ссылаться на элемент окружающего класса, то будет использована эта скрытая ссылка, что бы выбрать этот элемент. К счастью, компилятор заботится обо всех этих деталях вместо вас, но Вы все равно должны понимать, что этот объект внутреннего класса может быть создан только в соединении с объектом окружающего класса. Создание объекта внутреннего класса требует ссылки на объект окружающего его класса и компилятор выдаст сообщение об ошибке, если он не сможет получить доступ к этой ссылке. Наиболее часто это случается без какого либо вмешательства в эту часть программистом.
static внутренние классы
Если вам не нужно соединение между внутренним классом и объектом внешнего класса, тогда Вы можете сделать этот внутренний класс static. Для того, что бы понять значение static примененного к внутреннему классу, Вы должны вспомнить то, что этот объект обычного внутреннего класса неявным образом ссылается на объект создавшего его окружающего класса. А если Вы объявите его как static, то это уже не будет правдой. Static внутренний класс означает:
1. Вам не нужен объект внешнего класса для создания объекта static внутреннего класса.
2. Вы не можете получить доступ к внешнему объекту из static внутреннего класса.
Static внутренние классы отличаются от не-static внутренних классов. Поля и методы в не-static внутренних классах могут быть только на внешнем уровне класса, поэтому не-static внутренние классы не могут иметь static данные, static поля или static внутренние классы. Однако static внутренний класс не ограничен таким ограничением:
//: c08:Parcel10.java
// Static внутренний класс.
public class Parcel10 {
private static class PContents
implements Contents {
private int i = 11;
public int value() { return i; }
}
protected static class PDestination
implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
public String readLabel() { return label; }
// Static внутренний класс может содержать
// другие static элементы:
public static void f() {}
static int x = 10;
static class AnotherLevel {
public static void f() {}
static int x = 10;
}
}
public static Destination dest(String s) {
return new PDestination(s);
}
public static Contents cont() {
return new PContents();
}
public static void main(String[] args) {
Contents c = cont();
Destination d = dest("Tanzania");
}
} ///:~
В main( ), не требуется объекта Parcel10; вместо этого Вы используете нормальный синтаксис для выбора static элемента, что бы вызвать методы, которые возвращают ссылки на Contents и Destination.
Как Вы видели недавно, в обычном (не static) внутреннем классе, ссылка на объект внешнего класса достигается с помощью специальной ссылки this. Static внутренний класс не имеет этой специальной ссылки, что делает его аналогом static метода.
Обычно Вы не можете поместить какой либо код внутрь interface, но static внутренний класс может быть частью interface. Поскольку этот класс static, то он не нарушит правила для интерфейсов - static внутренний класс, только помещается он внутри поля имени интерфейса:
//: c08:IInterface.java
// Static внутренний класс внутри интерфейса.
interface IInterface {
static class Inner {
int i, j, k;
public Inner() {}
void f() {}
}
} ///:~
Раньше в этой книге, я предлагал помещать main( ) в каждый класс, что бы иметь возможность его тестировать. Препятствием для этого может служить, избыточный компилированный код. Если он сильно досаждает, то Вы можете использовать static внутренний класс для минимизации вашего тестового кода:
//: c08:TestBed.java
// Помещаем тестовый код в static внутренний класс.
class TestBed {
TestBed() {}
void f() { System.out.println("f()"); }
public static class Tester {
public static void main(String[] args) {
TestBed t = new TestBed();
t.f();
}
}
} ///:~
При этом создается класс называемый TestBed$Tester (для запуска программы, нужно использовать java TestBed$Tester). Вы можете его использовать для тестирования, но вовсе необязательно включать в поставку вашего продукта.
Ссылки на объект внешнего класса
Если вам необходимо сделать ссылку на внешний объект, вы просто указываете имя вашего внешнего объекта и после него добавляете точку и this. К примеру, в классе Sequence.SSelector, любые его методы могут производить хранимые ссылки на внешний класс Sequence просто указывая их как Sequence.this. Результирующая ссылка автоматически становится нужного типа. (Это обстоятельство известно и проверяется на стадии компилирования, поэтому на стадии выполнения не будет излишних накладных расходов.)
Иногда, вам нужно сообщить другим объектам, что бы они создали объекты своих внутренних классов. Что бы это провернуть, вам нужно предоставить ссылку другим внешним классам в выражении new, вот как в примере:
//: c08:Parcel11.java
// Создание экземпляров внутреннего класса.
public class Parcel11 {
class Contents {
private int i = 11;
public int value() { return i; }
}
class Destination {
private String label;
Destination(String whereTo) {
label = whereTo;
}
String readLabel() { return label; }
}
public static void main(String[] args) {
Parcel11 p = new Parcel11();
// Должны использовать экземпляр внешнего класса
// для создания экземпляра внутреннего класса:
Parcel11.Contents c = p.new Contents();
Parcel11.Destination d =
p.new Destination("Tanzania");
}
} ///:~
Для создания объекта внутреннего класса напрямую, Вы уже не должны следовать тем же самым методом и сослаться прямо на внешний класс Parcel11, но вместо этого Вы должны использовать объект внешнего класса для создания объекта внутреннего класса:
Parcel11.Contents c = p.new Contents();
Поэтому нельзя создать объект внутреннего класса до того, как у вас будет создан объект внешнего класса. Эта невозможность происходит из-за того, что внутренний класс "подсоединен" к объекту внешнего класса в котором он был создан. Тем не менее, если Вы сделаете static внутренний класс, то ему уже не нужна ссылка на внешний класс.
Доступ "наружу" из множественно вложенных классов
Совершенно не играет роли, как глубоко может быть вложен внутренний класс, он может совершенно прозрачно получить доступ ко всем элементам всех классов, в которых он вложен, ниже этому пример:
//: c08:MultiNestingAccess.java
// Вложенные классы могут получить доступ ко всем элементам
// всех классов, в которые они вложены.
class MNA {
private void f() {}
class A {
private void g() {}
public class B {
void h() {
g();
f();
}
}
}
}
public class MultiNestingAccess {
public static void main(String[] args) {
MNA mna = new MNA();
MNA.A mnaa = mna.new A();
MNA.A.B mnaab = mnaa.new B();
mnaab.h();
}
} ///:~
Вы можете видеть, что в MNA.A.B, методы g( ) и f( ) востребуются без каких либо ограничений (несмотря даже на тот факт, что они private). Этот пример также демонстрирует синтаксис требуемый для создания объектов многократно вложенных внутренних классов, когда Вы создаете объект в другом классе. New предоставляет правильную область действия, поэтому вам не нужно квалифицировать имя класса в вызове конструктора.
Наследование от внутренних классов
Поскольку конструктор внутреннего класса должен быть присоединен к ссылке на окружающий его класс, то наследование может выглядеть несколько запутано, если Вы захотите от него наследовать. Проблема заключается в том, что эта "секретная" ссылка на окружающий объект должна быть инициализирована, а в дочернем объекте нет того объекта, к которому можно было бы "привязаться". Решением здесь является использование синтаксиса явной ассоциации:
//: c08:InheritInner.java
// Наследование внутреннего класса.
class WithInner {
class Inner {}
}
public class InheritInner
extends WithInner.Inner {
//! InheritInner() {} // Не компилируется
InheritInner(WithInner wi) {
wi.super();
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner ii = new InheritInner(wi);
}
} ///:~
Вы можете видеть, что InheritInner расширяется только внутренним классом, но не расширяется внешним. Но когда приходит время для создания конструктора, то конструктор по умолчанию не подходит, Вы не можете передать ссылку на окружающий объект. В дополнение Вы должны использовать такой синтаксис enclosingClassReference.super(); внутри конструктора. При этом предоставляется ссылка и программа будет компилироваться.
Может ли быть внутренний класс перегружен?
Что произойдет, когда Вы создадите внутренний класс, а затем наследуете окружающий класс и переопределите внутренний класс? Что же это такое, что возможно переопределить внутренний класс? Да, это именно так и выглядит, но переопределение внутреннего класса это то же самое, как если бы во внешнем классе был бы другой метод, который ничего бы не делал:
//: c08:BigEgg.java
// Внутренний класс не может быть переопределен
// как метод.
class Egg {
protected class Yolk {
public Yolk() {
System.out.println("Egg.Yolk()");
}
}
private Yolk y;
public Egg() {
System.out.println("New Egg()");
y = new Yolk();
}
}
public class BigEgg extends Egg {
public class Yolk {
public Yolk() {
System.out.println("BigEgg.Yolk()");
}
}
public static void main(String[] args) {
new BigEgg();
}
} ///:~
Конструктор по умолчанию генерируется компилятором и эти вызовы конструктора базового класса тоже. Вы можете думать, что поскольку BigEgg уже создан, то будет использована переопределенная версия Yolk, но в действительности все не так. Вывод будет такой:
New Egg()
Egg.Yolk()
Этот пример показывает, что нет никакой дополнительной магии в наследовании от внешнего класса. Два внутренних классов полностью разделены, каждый существует в своем собственном пространстве имен. Но все равно, остается возможность недвусмысленно наследовать от внутреннего класса:
//: c08:BigEgg2.java
// Правильное наследование от внутреннего класса.
class Egg2 {
protected class Yolk {
public Yolk() {
System.out.println("Egg2.Yolk()");
}
public void f() {
System.out.println("Egg2.Yolk.f()");
}
}
private Yolk y = new Yolk();
public Egg2() {
System.out.println("New Egg2()");
}
public void insertYolk(Yolk yy) { y = yy; }
public void g() { y.f(); }
}
public class BigEgg2 extends Egg2 {
public class Yolk extends Egg2.Yolk {
public Yolk() {
System.out.println("BigEgg2.Yolk()");
}
public void f() {
System.out.println("BigEgg2.Yolk.f()");
}
}
public BigEgg2() { insertYolk(new Yolk()); }
public static void main(String[] args) {
Egg2 e2 = new BigEgg2();
e2.g();
}
} ///:~
Теперь BigEgg2.Yolk ясно расширяет Egg2.Yolk и переопределяет его методы. Метод insertYolk( ) позволяет BigEgg2 привести к базовому типу один из его объектов Yolk к ссылке y в Egg2, так что, когда g( ) вызывает y.f( ), то используется переопределенная версия f( ). Вывод же будет такой:
Egg2.Yolk()
New Egg2()
Egg2.Yolk()
BigEgg2.Yolk()
BigEgg2.Yolk.f()
Второй вызов Egg2.Yolk( ) это вызов конструктора базового класса из конструктора BigEgg2.Yolk. Вы так же можете видеть, что переопределенная версия f( ) используется, когда вызывается g( ).
Идентификаторы внутренних файлов
Поскольку, каждый класс создает файл .class, который содержит все информацию о том, как создавать объект этого типа, то Вы можете догадаться, что внутренний класс может так же создавать файл .class содержащий информацию для его объекта Class. Имя такого класса выбирается следующим образом: имя окружающего класса, символ "$", имя внутреннего класса. К примеру, файлы .class созданные InheritInner.java:
InheritInner.class
WithInner$Inner.class
WithInner.class
Если же внутренний класс - анонимный, то компилятор просто начинает генерировать для него идентификаторы. Если внутренний класс вложен во внутренний класс, то тогда их имена так же разделяются символом "$" и идентификатором внешнего класса.
Хотя эта схема генерирования внутренних имен проста и непосредственна, она так же устойчива к различным ситуациям. Поскольку такой подход к генерации, является подмножеством стандартной схемы именования в Java, то сгенерированные файлы становятся автоматически платформо-независимомы. (Заметьте, что компилятор изменяет ваши внутренние классы таким образом, что бы они работали.)
Зачем внутренние классы?
До этого момента Вы видели множество примеров синтаксиса и семантического описания работы внутренних классов, но еще не было ответа на вопрос "А зачем они собственно?". Зачем Sun пошел на такие значительные усилия, что бы добавить эту возможность языка?
Обычно внутренний класс или наследует от класса или реализует интерфейс, а код в нем манипулирует объектами внешнего класса, того, в котором он создан. Так что Вы можете предположить, что внутренние классы предоставляют этакую разновидность окна во внешнем классе.
Вопрос, глубоко задевающий внутренние классы: если мне нужна ссылка на интерфейс, почему я должен реализовывать его во внешнем классе? А вот и ответ: "Если это все , что нужно, то как этого добиться?" Так где разница, между реализацией интерфейса внутренним и внешним классами? А ответ такой - Вы не всегда можете удобно работать с интерфейсами, иногда нужно работать и с реализацией. Отсюда самая непреодолимая причина работы с классами:
Каждый внутренний класс может быть независимо наследован от реализации. Поэтому, внутренний класс не ограничен тем, если внешний класс уже наследовал от реализации.
Без способности внутренних классов наследовать более, чем от одного конкретного или абстрактного класса, некоторые проекты столкнулись бы с трудноразрешимыми проблемам. Так что единственное решение для внутренних классов, это проблема множественного наследования. То есть, внутренние классы эффективно позволяют вам наследовать больше чем от одного не интерфейса.
Что бы увидеть это более детально, представьте себе ситуацию, где у вас было бы два интерфейса, которые должны как-то выполниться внутри класса. В силу гибкости интерфейсов у вас есть два выбора: одиночный класс или внутренний класс:
//: c08:MultiInterfaces.java
// Два способа, как класс может
// реализовать множественные интерфейсы.
interface A {}
interface B {}
class X implements A, B {}
class Y implements A {
B makeB() {
// Анонимный внутренний класс:
return new B() {};
}
}
public class MultiInterfaces {
static void takesA(A a) {}
static void takesB(B b) {}
public static void main(String[] args) {
X x = new X();
Y y = new Y();
takesA(x);
takesA(y);
takesB(x);
takesB(y.makeB());
}
} ///:~
Естественно, что при этом логика вашего кода будет различна в обоих вариантах. Однако, обычно, Вы будете представлять себе в зависимости от проблемы, какой из способов предпочесть, одиночный класс или внутренний. Но безо всякого давления, в вышеприведенном примере, непонятно, какой из путей предпочесть. Оба из них работают.
Тем не менее, если у вас есть abstract или конкретный класс, вместо интерфейса, то Вы сразу же становитесь ограниченны в использовании только внутреннего класса, естественно, если все еще требуется реализовать их несколько в одном:
//: c08:MultiImplementation.java
// С конкретным или абстарктным классом, внутренние
// классы - единственный путь для достижения эффекта
// "множественная реализация интерфейса."
class C {}
abstract class D {}
class Z extends C {
D makeD() { return new D() {}; }
}
public class MultiImplementation {
static void takesC(C c) {}
static void takesD(D d) {}
public static void main(String[] args) {
Z z = new Z();
takesC(z);
takesD(z.makeD());
}
} ///:~
Если вам не нужно решать проблему с "множественной реализацией наследования", то вам лучше использовать какие угодно методы, кроме внутренних классов. Но с внутренними классами Вы получаете и дополнительные возможности:
1. Внутренний класс может иметь несколько экземпляров, каждый со своими собственными данными, независимыми от объекта внешнего класса.
2. В одном внешнем классе может быть несколько внутренних классов, каждый из которых реализует тот же самый интерфейс или наследует от того же самого класса, но по другому. Пример такого применения будет предоставлен дальше.
3. Место создания объекта внутреннего класса не связано с созданием объекта внешнего класса.
4. Не возникает потенциального конфликта в связи "это-есть" (is-a) с внутренним классом, поскольку они раздельные единицы.
Подобные документы
Разработка шаблонной функции FindMax, выполняющей поиск максимального элемента в массиве. Разработка шаблонного класса CMyArray, представляющего собой массив элементов некоторого типа. Иерархия классов и интерфейсов. Индексированный доступ к элементам.
лабораторная работа [419,0 K], добавлен 03.11.2014Изображение класса на диаграмме UML. Инкапсуляция как средство защиты его внутренних объектов. Использование принципа полиморфизма для реализации механизма интерфейсов. Создание новых классов путем наследования. Ассоциация как вид отношений между ними.
лекция [516,6 K], добавлен 03.12.2013Механизм классов в C++. Инициализация внутреннего объекта с помощью конструктора. Управление доступом к классу. Защищенные члены класса. Графические средства компилятора Borland C 3.1. Библиотека стандартных шаблонов. Реализация и использование класса.
курсовая работа [2,7 M], добавлен 16.05.2012Оценка функциональных возможностей стандартных классов представления данных на примерах использования избранных методов ("detect: ifNone:" класса Set, "to:by:do:" класса Number и "copy: ReplaceFrom: to: with:" класса OrderedCollection), их тестирование.
лабораторная работа [1,1 M], добавлен 14.10.2012Возможности использования Internet-ресурсов в средней школе. Мониторинг качества образовательных сайтов в России. Создание образовательного сайта по информатике для 10-го класса. Анализ практического использования образовательного сайта "Информатика".
дипломная работа [3,2 M], добавлен 10.03.2012Объединение данных с функциями их обработки в сочетании со скрытием ненужной для использования этих данных информации. Описание классов, их свойства. Спецификаторы доступа private и public. Доступ к элементам объекта. Сущность константного метода.
лабораторная работа [485,9 K], добавлен 22.10.2013Характеристика интерфейса в Java, возможность его расширения с использованием механизма наследования. Организация обратного вызова в Java. Сущность внутреннего класса. Обращение из внутреннего класса к элементам внешнего класса и листинг программы.
методичка [90,8 K], добавлен 30.06.2009Обзор технологии OpenStack, область ее применения. Реализация библиотеки классов. Реализация базовых классов и интерфейсов архитектуры. Создание виртуального сервера. Интеграция разработанной библиотеки классов и архитектура проектного решения.
дипломная работа [1,0 M], добавлен 09.08.2016Web-сервис как программная система, идентифицируемая с помощью некоторого URI, общедоступный интерфейс и связывания которого определяются и описываются с помощью языка описания интерфейсов WSDL. История, коммерческие предпосылки использования сервисов.
контрольная работа [169,1 K], добавлен 19.01.2012Анализ графических пользовательских интерфейсов современных систем оптимизации программ. Создание математической модели и алгоритма системы управления СБкЗ_ПП, ее архитектурно-контекстная диаграмма. Техническая документация программного средства.
дипломная работа [1,1 M], добавлен 18.04.2012