73

73


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

Поля в интерфейсах

Так как все поля, помещаемые в интерфейсе, автоматически являются статиче­скими (static) и неизменными (final), объявление interface хорошо подходит для создания групп постоянных значений. До выхода Java SE5 только так можно было имитировать перечисляемый тип enum из языков С и С++:

//• interfaces/Months java

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

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, которым присваиваются фиксированные значения на месте описания. Поля интерфейса автоматически являются открытыми (public), поэтому нет необходимости явно указывать это.

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

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

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

//• interfaces/RandVals java // Инициализация полей интерфейсов // не-константными выражениями, import java.util.*;

public interface RandVals {

Random RAND = new Random(47); int RANDOM_INT = RAND.nextInt(10); long RAND0M_L0NG = RAND.nextLong() * 10; float RANDOMJLOAT = RAND, next Long () * 10; double RAND0M_D0UBLE = RAND.nextDouble() * 10; } ///.-

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

//: interfaces/TestRandVals.java import static net.mindview.util.Print.*;

public class TestRandVals {

public static void main(String[] args) { print(RandVals.RANDOMJNT); pri nt(RandVals.RAND0M_L0NG); print(RandVals.RANDOM FLOAT); pri nt(RandVa1s.RANDOM^DOUBLE);

}

} /* Output: 8

-32032247016559954 -8.5939291E18 5.779976127815049 *///:-

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

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

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

//: interfaces/nesting/Nestinglnterfaces.java package interfaces.nesting;

class A {

interface В {

void f();

}

public class BImp implements В { public void f() {}

private class BImp2 implements В {продолжение &

public void f() {}

}

public interface С { void f();

}

class CImp implements С { public void f() {}

}

private class CImp2 implements С { 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 getDO { return new DImp2(); }

private D dRef;

public void receiveD(D d) { dRef = d; dRef.fO:

}

interface E {

interface G {

void f();

}

// Избыточное объявление public: public interface H { void f();

}

void g();

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

public class Nestinglnterfaces {

public class BImp implements А.В { public void f() {}

}

class CImp implements А С { 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.D. //! A D ad = a getDO. // He возвращает ничего, кроме A.D: //! A DImp2 di2 = a getDO. // Член интерфейса недоступен //' a getDO f().

// Только другой класс А может использовать getDO А а2 = new А(). а2 receiveD(a getDO).

}

} /// ~

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

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

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

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