русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Инициализация полей интерфейсов


Дата добавления: 2015-06-12; просмотров: 1104; Нарушение авторских прав


Поля, определяемые в интерфейсах, не могут быть «пустыми константами», но могут инициализироваться неконстантными выражениями. Например:

//: interfaces/RandVals.java

// Инициализация полей интерфейсов

// не-константными выражениями

import java.util.*;

 

public interface RandVals {

Random RAND = new Random(47);

int RANDOM_INT = RAND.nextInt(10);

long RANDOM_LONG = RAND.nextLong() * 10;

float RANDOM_FLOAT = RAND.nextLong() * 10;

double RANDOM_DOUBLE = RAND.nextDouble() * 10;

}

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

//: interfaces/TestRandVals.java

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

 

public class TestRandVals {

public static void main(String[] args) {

print(RandVals.RANDOM_INT);

print(RandVals.RANDOM_LONG);

print(RandVals.RANDOM_FLOAT);

print(RandVals.RANDOM_DOUBLE);

}

}

<spoiler text="Output:">

-32032247016559954

-8.5939291E18

5.779976127815049

</spoiler> Конечно, поля не являются частью интерфейса. Данные хранятся в статической области памяти, отведенной для данного интерфейса.

 

Вложенные интерфейсы

Интерфейсы могут вкладываться в классы и в другие интерфейсы. При этом обнаруживается несколько весьма интересных особенностей:

//: interfaces/nesting/NestingInterfaces.java

package interfaces.nesting;

 

class A {

interface B {

void f();

}

public class BImp implements B {

public void f() {}

}

private class BImp2 implements B {

public void f() {}

}

public interface C {

void f();

}

class CImp implements C {

public void f() {}

}

private class CImp2 implements C {

public void f() {}

}

private interface D {



void f();

}

private class DImp implements D {

public void f() {}

}

public class DImp2 implements D {

public void f() {}

}

public D getD() { return new DImp2(); }

private D dRef;

public void receiveD(D d) {

dRef = d;

dRef.f();

}

}

 

interface E {

interface G {

void f();

}

// Избыточное объявление public:

public interface H {

void f();

}

void g();

// He может быть private внутри интерфейса:

//! private interface I {}

}

 

public class NestingInterfaces {

public class BImp implements A.B {

public void f() {}

}

class CImp implements A.C {

public void f() {}

}

// Private-интерфейс не может быть реализован нигде,

// кроме как внутри класса, где он был определен:

//! class DImp implements A.D {

//! public void f() {}

//! }

class EImp implements E {

public void g() {}

}

class EGImp implements E.G {

public void f() {}

}

class EImp2 implements E {

public void g() {}

class EG implements E.G {

public void f() {}

}

}

public static void main(String[] args) {

A a = new A();

// Нет доступа к A.D:

//! A.D ad = a.getD();

// He возвращает ничего, кроме A.D:

//! A.DImp2 di2 = a.getD();

// Член интерфейса недоступен:

//! a.getD().f();

// Только другой объект класса А может использовать getD():

A a2 = new A();

a2.receiveD(a.getD());

}

}

Синтаксис вложения интерфейса в класс достаточно очевиден. Вложенные интерфейсы, как и обычные, могут иметь «пакетную» или открытую (public) видимость.

Любопытная подробность: интерфейсы могут быть объявлены закрытыми (private), как видно на примере A.D (используется тот же синтаксис описания, что и для вложенных классов). Для чего нужен закрытый вложенный интерфейс?

Может показаться, что такой интерфейс реализуется только в виде закрытого (private) вложенного класса, подобного DImp, но A.DImp2 показывает, что он также может иметь форму открытого (public) класса. Тем не менее класс A.DImp2 «замкнут» сам на себя. Факт реализации private-интерфейса не может упоминаться в программе, поэтому реализация такого интерфейса — просто способ принудительного определения методов этого интерфейса без добавления информации о дополнительном типе (то есть восходящее преобразование становится невозможным).

Метод getD() усугубляет сложности, связанные с private-интерфейсом, — это открытый (public) метод, возвращающий ссылку на закрытый (private) интерфейс. Что можно сделать с возвращаемым значением этого метода? В методе main() мы видим несколько попыток использовать это возвращаемое значение, и все они оказались неудачными. Заставить метод работать можно только одним способом — передать возвращаемое значение некоторому объекту, которому разрешено его использование (в нашем случае это еще один объект А, у которого имеется необходимый метод receiveD()).

Интерфейс Е показывает, что интерфейсы могут быть вложены друг в друга. Впрочем, правила для интерфейсов — в особенности то, что все элементы интерфейса должны быть открытыми (public), — здесь строго соблюдаются, поэтому интерфейс, вложенный внутрь другого интерфейса, автоматически объявляется открытым и его нельзя сделать закрытым (private).

Пример NestingInterfaces демонстрирует разнообразные способы реализации вложенных интерфейсов. Особо стоит отметить тот факт, что при реализации интерфейса вы не обязаны реализовывать вложенные в него интерфейсы. Также закрытые (private) интерфейсы нельзя реализовать за пределами классов, в которых они описываются.

 

Интерфейсы и фабрики

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

//: interfaces/Factories.java

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

 

interface Service {

void method1();

void method2();

}

 

interface ServiceFactory {

Service getService();

}

 

class Implementation1 implements Service {

Implementation1() {} // Доступ в пределах пакета

public void method1() {print("Implementation1 method1");}

public void method2() {print("Implementation1 method2");}

}

 

class Implementation1Factory implements ServiceFactory {

public Service getService() {

return new Implementation1();

}

}

 

class Implementation2 implements Service {

Implementation2() {} // Доступ в пределах пакета

public void method1() {print("Implementation2 method1");}

public void method2() {print("Implementation2 method2");}

}

 

class Implementation2Factory implements ServiceFactory {

public Service getService() {

return new Implementation2();

}

}

 

public class Factories {

public static void serviceConsumer(ServiceFactory fact) {

Service s = fact.getService();

s.method1();

s.method2();

}

public static void main(String[] args) {

serviceConsumer(new Implementation1Factory());

// Реализации полностью взаимозаменяемы:

serviceConsumer(new Implementation2Factory());

}

}

<spoiler text="Output:">

Implementation1 method1

Implementation1 method2

Implementation2 method1

Implementation2 method2

</spoiler> Без применения фабрики вам пришлось бы где-то указать точный тип создаваемого объекта Service, чтобы он мог вызвать подходящий конструктор.

Но зачем вводить лишний уровень абстракции? Данный паттерн часто применяется при создании библиотек. Допустим, вы создаете систему для игр, которая позволяла бы играть в шашки и шахматы на одной доске:

//: interfaces/Games.java

// Игровая библиотека с использованием фабрики

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

 

interface Game { boolean move(); }

interface GameFactory { Game getGame(); }

 

class Checkers implements Game {

private int moves = 0;

private static final int MOVES = 3;

public boolean move() {

print("Checkers move " + moves);

return ++moves != MOVES;

}

}

 

class CheckersFactory implements GameFactory {

public Game getGame() { return new Checkers(); }

}

 

class Chess implements Game {

private int moves = 0;

private static final int MOVES = 4;

public boolean move() {

print("Chess move " + moves);

return ++moves != MOVES;

}

}

 

class ChessFactory implements GameFactory {

public Game getGame() { return new Chess(); }

}

 

public class Games {

public static void playGame(GameFactory factory) {

Game s = factory.getGame();

while(s.move())

;

}

public static void main(String[] args) {

playGame(new CheckersFactory());

playGame(new ChessFactory());

}

}

<spoiler text="Output:">

Checkers move 0

Checkers move 1

Checkers move 2

Chess move 0

Chess move 1

Chess move 2

Chess move 3

</spoiler> Если класс Games представляет сложный блок кода, такое решение позволит повторно использовать его для разных типов игр. В следующей главе будет представлен более элегантный способ реализации фабрик на базе анонимных внутренних классов.

 

Резюме

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

Многие программисты поддались этому искушению. Тем не менее любая абстракция должна быть мотивирована реальной потребностью. Основное назначение интерфейсов — возможность переработки реализации в случае необходи­мости, а не введение лишнего уровня абстракции вместе с дополнительными сложностями. Дополнительные сложности могут оказаться довольно существенными. А представьте, что кто-то будет вынужден разбираться в вашем коде и в конечном итоге поймет, что интерфейсы были добавлены «на всякий случай», без веских причин — в таких ситуациях все проектирование, которое выполнялось данным разработчиком, начинает выглядеть довольно сомнительно.

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


 



<== предыдущая лекция | следующая лекция ==>
InterfaceCollіsi on java:24- интерфейсы І1 и I1 несовместимы; оба определяют f(), но с различными возвращаемыми типами | Глава 10 ВНУТРЕННИЕ КЛАССЫ


Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.

Генерация страницы за: 1.638 сек.