Рейтинговые книги
Читем онлайн Философия Java3 - Брюс Эккель

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 50 51 52 53 54 55 56 57 58 ... 132

}

}; // Требуется точка с запятой

}

public static void main(String[] args) { Parcel8 p = new Parcel80; Wrapping w = p.wrapping(lO);

}

} ///:-

Требуемый аргумент просто передается в конструктор базового класса, как в рассмотренном примере х в выражении new Wrapping(x). Хотя это обычный класс с реализацией, Wrapping также используется в качестве общего «интерфейса» для своих производных классов:

II: innerclasses/Wrapping.java public class Wrapping { private int i,

public Wrapping(int x) { i = x; } public int valueO { return i; } } ///:-

Класс Wrapping имеет конструктор с аргументом — просто для того, чтобы ситуация стала чуть более интересной.

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

Инициализацию также можно провести в точке определения полей безымянного класса:

II: innerclasses/Parcel9.java

II Безымянный внутренний класс, выполняющий инициализацию. II Более короткая версия программы Parcel5 java

public class Parcel9 {

II Для использования в безымянном внутреннем классе II аргументы должны быть неизменны (final); public Destination destination(final String dest) { return new Destination0 {

private String label = dest;

public String readLabelO { return label; }

}:

}

public static void main(String[] args) { Parcel9 p = new Parcel90; Destination d = p.destinationCTacMaHHfl").

}

Если вы определяете безымянный внутренний класс и хотите при этом использовать объекты, определенные вне этого внутреннего класса, компилятор требует, чтобы переданные на них ссылкй объявлялись неизменными (final), как это сделано аргументе destination(). Без такого объявления вы получите сообщение об ошибке при компиляции программы.

Пока мы ограничиваемся простым присваиванием значений полям, указанный подход работает. А если понадобится выполнить некоторые действия, свойственные конструкторам? В безымянном классе именованный конструктор определить нельзя (раз у самого класса нет имени!), но инициализация экземпляра (instance initialization) фактически позволяет добиться желаемого эффекта:

//. innerclasses/AnonymousConstructor.java

II Создание конструктора для безымянного внутреннего класса.

import static net.mindview.util.Print.*,

abstract class Base {

public Base(int i) {

print("Конструктор Base, i = " + i);

}

public abstract public void f();

}

public class AnonymousConstructor {

public static Base getBase(int i) { return new Base(i) {

{ рпгйС'Инициализация экземпляра"); } public void f() {

print ("Безымянный fO").

}

}.

}

public static void main(String[] args) { Base base = getBase(47); base.fO;

}).

}

} /* Output.

Конструктор Base, i = 47

Инициализация экземпляра

Безымянный f()

*///.-

В таком случае переменная i не обязана быть неизменной (final). И хотя i передается базовому конструктору безымянного класса, она никогда не используется напрямую внутри безымянного класса.

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

II- innerclasses/ParcellO.java

II Демонстрация "инициализации экземпляра" для

II конструирования безымянного внутреннего класса. продолжение &

public class Parcel 10 { public Destination

destination(final String dest, final float price) { return new Destination() { private int cost,

// Инициализация экземпляра для каждого объекта. {

cost = Math round(price), if(cost > 100)

System out println("Превышение бюджета!"),

}

private String label = dest,

public String readLabelO { return label, }

}.

}

public static void main(String[] args) {

Parcel 10 p = new Parcel 100.

Destination d = p destination"Тасмания". 101 395F),

}

} /* Output-Превышение бюджета! */// -

Внутри инициализатора экземпляра виден код, недоступный при инициализации полей (то есть команда if). Поэтому инициализатор экземпляра фактически является конструктором безымянного внутреннего класса. Конечно, возможности его ограничены; перегружать такой инициализатор нельзя, и поэтому он будет присутствовать в классе только в единственном числе.

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

Снова о методе-фабрике

Посмотрите, насколько приятнее выглядит пример interfaces/Factories.java при использовании безымянных внутренних классов:

// innerclasses/Factories java import static net.mindview util Print *;

interface Service { void methodic), void method2(),

}

interface SemceFactory { Service getServiceO;

}

class Implementationl implements Service { private ImplementationlO {}

public void methodic) {printC"Implementationl methodl");} public void method2() {print("Implementationl method2");}

public static ServiceFactory factory = new ServiceFactoryO {

public Service getServiceO {

return new ImplementationlO;

class Implementation2 implements Service { private Implementation20 {}

public void methodlO {print("Implementation2 methodl"),) public void method2() {print("Implementation2 method2"),j public static ServiceFactory factory = new ServiceFactoryO {

public Service getServiceO {

return new Implementation2();

public class Factories {

public static void serviceConsumer(ServiceFactory fact) { Service s = fact.getServiceO; s methodlO; s method2();

}

public static void main(String[] args) {

serviceConsumer(Implementationl.factory); // Реализации полностью взаимозаменяемы: servi ceConsumer(Implementati on2.factory);

}

} /* Output-Implementationl methodl Implementationl method2 Implementation2 methodl Implementation2 method2 */// ~

Теперь конструкторы Implementationl и Implementation2 могут быть закрытыми, и фабрику необязательно оформлять в виде именованного класса. Кроме того, часто бывает достаточно одного фабричного объекта, поэтому в данном случае он создается как статическое поле в реализации Service. Наконец, итоговый синтаксис выглядит более осмысленно.

Пример interfaces/Games.java тоже можно усовершенствовать с помощью безымянных внутренних классов:

//. innerclasses/Games.java

// Использование анонимных внутренних классов в библиотеке Game import static net.mindview.util.Print.*;

interface Game { boolean moveO; } interface GameFactory { Game getGameO; }

class Checkers implements Game { private Checkers О {} private int moves = 0;

private static final int MOVES = 3; продолжение &

class Chess implements Game { private ChessO {} private int moves = 0; private static final int MOVES = 4; public boolean moveO {

print("Chess move " + moves); return ++moves != MOVES;

}

public static GameFactory factory = new GameFactoryO { public Game getGameO { return new ChessO; }

}:

}

public class Games {

public static void piayGame(GameFactory factory) { Game s = factory.getGameO; while(s. moveO)

}

public static void main(String[] args) { pi ayGame(Checkers.factory); piayGame(Chess.factory);

}

} /* Output: Checkers move 0 Checkers move 1 Checkers move 2 Chess move 0 Chess move 1 Chess move 2 Chess move 3 *///•-

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

Вложенные классы

public boolean moveО {

print("Checkers move " + moves); return ++moves != MOVES;

}

public static GameFactory factory = new GameFactoryO { public Game getGameO { return new Checkers О; }

Если связь между объектом внутреннего класса и объектом внешнего класса не нужна, можно сделать внутренний класс статическим (объявить его как static). Часто такой класс называют вложенным15 (nested). Чтобы понять смысл ключевого слова static в отношении внутренних классов, следует вспомнить, что в объекте обычного внутреннего класса тайно хранится ссылка на объект создавшего его объемлющего внешнего класса. При использовании статического внутреннего класса такой ссылки не существует. Применение статического внутреннего класса означает следующее:

• для создания объекта статического внутреннего класса не нужен объект внешнего класса;

• из объекта вложенного класса нельзя обращаться к не-статическим членам внешнего класса.

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

//• i nnerclasses/Parcel 11.java

// Вложенные (статические внутренние) классы

public class Parcel 11 {

private static class PContents implements Contents { private int i = 11; public int valueO { return i; }

}

protected static class ParcelDestination implements Destination { private String label;

private Parcel Destination(String whereTo) { label = whereTo,

}

public String readLabelO { return label; }

// Вложенные классы могут содержать другие статические элементы;

public static void f() {}

static int x = 10;

static class AnotherLevel {

public static void f() {} static int x = 10;

}

}

public static Destination destination(String s) { return new ParcelDestination(s);

}

public static Contents contO {

return new Parcel Contents О,

}

public static void main(String[] args) { Contents с = contentsO, Destination d = destinationC'TacMaHHfl"),

}

} ///;-

В методе main() не требуется объекта класса Parcelll; вместо этого для вызова методов, возвращающих ссылки на Contents и Destination, используется обычный синтаксис обращения к статическим членам класса.

Как было сказано ранее, в обычном (не-статическом) внутреннем классе для обращения к объекту внешнего класса используется специальная ссылка this. Во вложенном классе такая ссылка недействительна (по аналогии со статическими методами).

1 ... 50 51 52 53 54 55 56 57 58 ... 132
На этой странице вы можете бесплатно читать книгу Философия Java3 - Брюс Эккель бесплатно.
Похожие на Философия Java3 - Брюс Эккель книги

Оставить комментарий