русс | укр

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

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

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

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


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

Глава 9 ИНТЕРФЕЙСЫ


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


Содержание · 1 ИНТЕРФЕЙСЫ o 1.1 Абстрактные классы и методы o 1.2 Интерфейсы o 1.3 Отделение интерфейса от реализации o 1.4 «Множественное наследование» в Java o 1.5 Расширение интерфейса через наследование o 1.6 Конфликты имен при совмещении интерфейсов o 1.7 Интерфейсы как средство адаптации o 1.8 Поля в интерфейсах § 1.8.1 Объявление полей интерфейсов § 1.8.2 Инициализация полей интерфейсов o 1.9 Вложенные интерфейсы o 1.10 Интерфейсы и фабрики o 1.11 Резюме

Интерфейсы и абстрактные классы улучшают структуру кода и способствуют отделению интерфейса от реализации. В традиционных языках программирования такие механизмы не получили особого распространения. Например, в C++ существует лишь косвенная поддержка этих концепций. Сам факт их существования в Java показывает, что эти концепции были сочтены достаточно важными для прямой поддержки в языке.

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

Абстрактные классы и методы

В примере с классами музыкальных инструментов из предыдущей главы методы базового класса Instrument всегда оставались «фиктивными». Попытка вызова такого метода означала, что в программе произошла какая-то ошибка. Это объяснялось тем, что класс Instrument создавался для определения общего интерфейса всех классов, производных от него.

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



Если в программе определяется абстрактный класс вроде Instrument, создание объектов такого класса практически всегда бессмысленно. Абстрактный класс создается для работы с набором классов через общий интерфейс.

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

В языке Java для решения подобных задач применяются абстрактные методы. Абстрактный метод незавершен; он состоит только из объявления и не имеет тела. Синтаксис объявления абстрактных методов выглядит так:

abstract void f();

Класс, содержащий абстрактные методы, называется абстрактным классом. Такие классы тоже должны помечаться ключевым словом abstract (в противном случае компилятор выдает сообщение об ошибке).

Если вы объявляете класс, производный от абстрактного класса, но хотите иметь возможность создания объектов нового типа, вам придется предоставить определения для всех абстрактных методов базового класса. Если этого не сделать, производный класс тоже останется абстрактным, и компилятор заставит пометить новый класс ключевым словом abstract.

Можно создавать класс с ключевым словом abstract даже тогда, когда в нем не имеется ни одного абстрактного метода. Это бывает полезно в ситуациях, где в классе абстрактные методы просто не нужны, но необходимо запретить создание экземпляров этого класса.

Класс Instrument очень легко можно сделать абстрактным. Только некоторые из его методов станут абстрактными, поскольку объявление класса как abstract не подразумевает, что все его методы должны быть абстрактными. Вот что по­лучится:


Файл:P0222.png

А вот как выглядит реализация примера оркестра с использованием абстрактных классов и методов:

//: interfaces/music4/Music4.java

// Абстрактные классы и методы

package interfaces.music4;

import polymorphism.music.Note;

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

 

abstract class Instrument {

private int i; // Память выделяется для каждого объекта

public abstract void play(Note n);

public String what() { return "Instrument"; }

public abstract void adjust();

}

 

class Wind extends Instrument {

public void play(Note n) {

print("Wind.play() " + n);

}

public String what() { return "Wind"; }

public void adjust() {}

}

 

class Percussion extends Instrument {

public void play(Note n) {

print("Percussion.play() " + n);

}

public String what() { return "Percussion"; }

public void adjust() {}

}

 

class Stringed extends Instrument {

public void play(Note n) {

print("Stringed.play() " + n);

}

public String what() { return "Stringed"; }

public void adjust() {}

}

 

class Brass extends Wind {

public void play(Note n) {

print("Brass.play() " + n);

}

public void adjust() { print("Brass.adjust()"); }

}

 

class Woodwind extends Wind {

public void play(Note n) {

print("Woodwind.play() " + n);

}

public String what() { return "Woodwind"; }

}

 

public class Music4 {

// Работа метода не зависит от фактического типа объекта.

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

static void tune(Instrument i) {

// ...

i.play(Note.MIDDLE_C);

}

static void tuneAll(Instrument[] e) {

for(Instrument i : e)

tune(i);

}

public static void main(String[] args) {

// Восходящее преобразование при добавлении в массив

Instrument[] orchestra = {

new Wind(),

new Percussion(),

new Stringed(),

new Brass(),

new Woodwind()

};

tuneAll(orchestra);

}

}

<spoiler text="Output:">

Wind.play() MIDDLE_C

Percussion.play() MIDDLE_C

Stringed.play() MIDDLE_C

Brass.play() MIDDLE_C

Woodwind.play() MIDDLE_C

</spoiler> Как видите, объем изменений минимален. Создавать абстрактные классы и методы полезно, так как они подчеркивают абстрактность класса, а также сообщают и пользователю класса, и компилятору, как следует с ним обходиться. Кроме того, абстрактные классы играют полезную роль при переработке программ, потому что они позволяют легко перемещать общие методы вверх по иерархии наследования.

Интерфейсы

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

Ключевое слово interface фактически означает: «Именно так должны выглядеть все классы, которые реализуют данный интерфейс». Таким образом, любой код, использующий конкретный интерфейс, знает только то, какие методы вызываются для этого интерфейса, но не более того. Интерфейс определяет своего рода «протокол взаимодействия» между классами. Однако интерфейс представляет собой нечто большее, чем абстрактный класс в своем крайнем проявлении, потому что он позволяет реализовать подобие «множественного наследования» C++: иначе говоря, создаваемый класс может быть преобразован к нескольким базовым типам.

Чтобы создать интерфейс, используйте ключевое слово interface вместо class. Как и в случае с классами, вы можете добавить перед словом interface спецификатор доступа public (но только если интерфейс определен в файле, имеющем то же имя) или оставить для него дружественный доступ, если он будет использоваться только в пределах своего пакета. Интерфейс также может содержать поля, но они автоматически являются статическими (static) и неизменными (final).

Для создания класса, реализующего определенный интерфейс (или группу интерфейсов), используется ключевое слово implements. Фактически оно означает: «Интерфейс лишь определяет форму, а сейчас будет показано, как это работает». В остальном происходящее выглядит как обычное наследование. Рассмотрим реализацию на примере иерархии классов Instrument:

Файл:P0225.png

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

При описании методов в интерфейсе вы можете явно объявить их открытыми (public), хотя они являются таковыми даже без спецификатора. Однако при реализации интерфейса его методы должны быть объявлены как public. В противном случае будет использоваться доступ в пределах пакета, а это приведет к уменьшению уровня доступа во время наследования, что запрещается компилятором Java.

Все сказанное можно увидеть в следующем примере с объектами Instrument. Заметьте, что каждый метод интерфейса ограничивается простым объявлением; ничего большего компилятор не" разрешит. Вдобавок ни один из методов интер­фейса Instrument не объявлен со спецификатором public, но все методы автоматически являются открытыми:

//: interfaces/music5/Music5.java

// Интерфейсы.

package interfaces.music5;

import polymorphism.music.Note;

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

 

interface Instrument {

// Константа времени компиляции:

int VALUE = 5; // static & final

// Определения методов недопустимы:

void play(Note n); // Автоматически объявлен как public

}

 

class Wind implements Instrument {

public void play(Note n) {

print(this + ".play() " + n);

}

public String toString() { return "Wind"; }

public void adjust() { print(this + ".adjust()"); }

}

 

class Percussion implements Instrument {

public void play(Note n) {

print(this + ".play() " + n);

}

public String toString() { return "Percussion"; }

public void adjust() { print(this + ".adjust()"); }

}

 

class Stringed implements Instrument {

public void play(Note n) {

print(this + ".play() " + n);

}

public String toString() { return "Stringed"; }

public void adjust() { print(this + ".adjust()"); }

}

 

class Brass extends Wind {

public String toString() { return "Brass"; }

}

 

class Woodwind extends Wind {

public String toString() { return "Woodwind"; }

}

 

public class Music5 {

// Работа метода не зависит от фактического типа объекта.

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

static void tune(Instrument i) {

// ...

i.play(Note.MIDDLE_C);

}

static void tuneAll(Instrument[] e) {

for(Instrument i : e)

tune(i);

}

public static void main(String[] args) {

// Восходящее преобразование при добавлении в массив:

Instrument[] orchestra = {

new Wind(),

new Percussion(),

new Stringed(),

new Brass(),

new Woodwind()

};

tuneAll(orchestra);

}

}

<spoiler text="Output:">

Wind.play() MIDDLE_C

Percussion.play() MIDDLE_C

Stringed.play() MIDDLE_C

Brass.play() MIDDLE_C

Woodwind.play() MIDDLE_C

</spoiler> В этой версии присутствует еще одно изменение: метод what() был заменен на toString(). Так как метод toString() входит в корневой класс Object, его присутствие в интерфейсе не обязательно.

Остальной код работает так же, как прежде. Неважно, проводите ли вы преобразование к «обычному» классу с именем Instrument, к абстрактному классу с именем Instrument или к интерфейсу с именем Instrument — действие будет одинаковым. В методе tune() ничто не указывает на то, является класс Instrument «обычным» или абстрактным, или это вообще не класс, а интерфейс.

Отделение интерфейса от реализации

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

Представьте, что у нас имеется класс Processor с методами name() и process(). Последний получает входные данные, изменяет их и выдает результат. Базовый класс расширяется для создания разных специализированных типов Processor. В следующем примере производные типы изменяют объекты String (обратите внимание: ковариантными могут быть возвращаемые значения, но не типы аргументов):

//: interfaces/classprocessor/Apply.java

package interfaces.classprocessor;

import java.util.*;

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

 

class Processor {

public String name() {

return getClass().getSimpleName();

}

Object process(Object input) { return input; }

}

 

class Upcase extends Processor {

String process(Object input) { // Ковариантный возвращаемый тип

return ((String)input).toUpperCase();

}

}

 

class Downcase extends Processor {

String process(Object input) {

return ((String)input).toLowerCase();

}

}

 

class Splitter extends Processor {

String process(Object input) {

// Аргумент split() используется для разбиения строки

return Arrays.toString(((String)input).split(" "));

}

}

 

public class Apply {

public static void process(Processor p, Object s) {

print("Using Processor " + p.name());

print(p.process(s));

}

public static String s =

"Disagreement with beliefs is by definition incorrect";

public static void main(String[] args) {

process(new Upcase(), s);

process(new Downcase(), s);

process(new Splitter(), s);

}

}

<spoiler text="Output:">

Using Processor Upcase

DISAGREEMENT WITH BELIEFS IS BY DEFINITION INCORRECT

Using Processor Downcase

disagreement with beliefs is by definition incorrect

Using Processor Splitter

[Disagreement, with, beliefs, is, by, definition, incorrect]

</spoiler> Метод Apply.process() получает любую разновидность Processor, применяет ее к Object, а затем выводит результат. Метод split() является частью класса String. Он получает объект String, разбивает его на несколько фрагментов по ограничи­телям, определяемым переданным аргументом, и возвращает String[ ]. Здесь он используется как более компактный способ создания массива String. Теперь предположим, что вы обнаружили некое семейство электронных фильтров, которые тоже было бы уместно использовать с методом Apply. process():

//: interfaces/filters/Waveform.java

package interfaces.filters;

public class Waveform {

private static long counter;

private final long id = counter++;

public String toString() { return "Waveform " + id; }

}

 

//: interfaces/filters/Filter.java

package interfaces.filters;

public class Filter {

public String name() {

return getClass().getSimpleName();

}

public Waveform process(Waveform input) { return input; }

}

 

//: interfaces/filters/LowPass.java

package interfaces.filters;

public class LowPass extends Filter {

double cutoff;

public LowPass(double cutoff) { this.cutoff = cutoff; }

public Waveform process(Waveform input) {

return input; //Фиктивная обработка

}

}

 

//: interfaces/filters/HighPass.java

package interfaces.filters;

public class HighPass extends Filter {

double cutoff;

public HighPass(double cutoff) { this.cutoff = cutoff; }

public Waveform process(Waveform input) { return input; }

}

 

//: interfaces/filters/BandPass.java

package interfaces.filters;

public class BandPass extends Filter {

double lowCutoff, highCutoff;

public BandPass(double lowCut, double highCut) {

lowCutoff = lowCut;

highCutoff = highCut;

}

public Waveform process(Waveform input) { return input; }

}

Класс Filter содержит те же интерфейсные элементы, что и Processor, но, поскольку он не является производным от Processor (создатель класса Filter и не подозревал, что вы захотите использовать его как Processor), он не может исполь­зоваться с методомApply.process(), хотя это выглядело бы вполне естественно.

Логическая привязка между Apply.process() и Processor оказывается более сильной, чем реально необходимо, и это обстоятельство препятствует повторному ис­пользованию кода Apply.process(). Также обратите внимание, что входные и выходные данные относятся к типуWaveform. Но, если преобразовать класс Processor в интерфейс, ограничения ослабляются и появляется возможность повторного использования Apply.process(). Обновленные версии Processor и Apply выглядят так:

//: interfaces/interfaceprocessor/Processor.java

package interfaces.interfaceprocessor;

public interface Processor {

String name();

Object process(Object input);

}

 

//: interfaces/interfaceprocessor/Apply.java

package interfaces.interfaceprocessor;

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

public class Apply {

public static void process(Processor p, Object s) {

print("Using Processor " + p.name());

print(p.process(s));

}

}

В первом варианте повторного использования кода клиентские программисты пишут свои классы с поддержкой интерфейса:

//: interfaces/interfaceprocessor/StringProcessor.java

package interfaces.interfaceprocessor;

import java.util.*;

 

public abstract class StringProcessor implements Processor{

public String name() {

return getClass().getSimpleName();

}

public abstract String process(Object input);

public static String s =

"If she weighs the same as a duck, she's made of wood";

public static void main(String[] args) {

Apply.process(new Upcase(), s);

Apply.process(new Downcase(), s);

Apply.process(new Splitter(), s);

}

}

 

class Upcase extends StringProcessor {

public String process(Object input) { // Covariant return

return ((String)input).toUpperCase();

}

}

 

class Downcase extends StringProcessor {

public String process(Object input) {

return ((String)input).toLowerCase();

}

}

 

class Splitter extends StringProcessor {

public String process(Object input) {

return Arrays.toString(((String)input).split(" "));

}

}

<spoiler text="Output:">

Using Processor Upcase

IF SHE WEIGHS THE SAME AS A DUCK, SHE'S MADE OF WOOD

Using Processor Downcase

if she weighs the same as a duck, she's made of wood

Using Processor Splitter

[If, she, weighs, the, same, as, a, duck,, she's, made, of, wood]

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

//: interfaces/interfaceprocessor/FilterProcessor.java

package interfaces.interfaceprocessor;

import interfaces.filters.*;

 

class FilterAdapter implements Processor {

Filter filter;

public FilterAdapter(Filter filter) {

this.filter = filter;

}

public String name() { return filter.name(); }

public Waveform process(Object input) {

return filter.process((Waveform)input);

}

}

 

public class FilterProcessor {

public static void main(String[] args) {

Waveform w = new Waveform();

Apply.process(new FilterAdapter(new LowPass(1.0)), w);

Apply.process(new FilterAdapter(new HighPass(2.0)), w);

Apply.process(

new FilterAdapter(new BandPass(3.0, 4.0)), w);

}

}

<spoiler text="Output:">

Using Processor LowPass

Waveform 0

Using Processor HighPass

Waveform 0

Using Processor BandPass

Waveform 0

</spoiler> Конструктор FilterAdapter получает исходный интерфейс (Filter) и создает объект с требуемым интерфейсом Processor. Также обратите внимание на применение делегирования в классе FilterAdapter. Отделение интерфейса от реализации позволяет применять интерфейс к разным реализациям, а следовательно, расширяет возможности повторного использования кода.

«Множественное наследование» в Java

Так как интерфейс по определению не имеет реализации (то есть не обладает памятью для хранения данных), нет ничего, что могло бы помешать совмещению нескольких интерфейсов. Это очень полезная возможность, так как в некоторых ситуациях требуется выразить утверждение: «Икс является и А, и Б, и В одновременно». В C++ подобное совмещение интерфейсов нескольких классов называется множественным наследованием, и оно имеет ряд очень неприятных аспектов, поскольку каждый класс может иметь свою реализацию. В Javaможно добиться аналогичного эффекта, но, поскольку реализацией обладает всего один класс, проблемы, возникающие при совмещении нескольких интерфейсов в C++, в Java принципиально невозможны:

Файл:P0232.png

При наследовании базовый класс вовсе не обязан быть абстрактным или «реальным» (без абстрактных методов). Если наследование действительно осуществляется не от интерфейса, то среди прямых «предков» класс может быть только один — все остальные должны быть интерфейсами. Имена интерфейсов перечисляются вслед за ключевым словом implements и разделяются запятыми. Интерфейсов может быть сколько угодно, причем к ним можно проводить восходящее преобразование. Следующий пример показывает, как создать новый класс на основе реального класса и нескольких интерфейсов:

//: interfaces/Adventure.java

// Использование нескольких интерфейсов.

 

interface CanFight {

void fight();

}

 

interface CanSwim {

void swim();

}

 

interface CanFly {

void fly();

}

 

class ActionCharacter {

public void fight() {}

}

 

class Hero extends ActionCharacter

implements CanFight, CanSwim, CanFly {

public void swim() {}

public void fly() {}

}

 

public class Adventure {

public static void t(CanFight x) { x.fight(); }

public static void u(CanSwim x) { x.swim(); }

public static void v(CanFly x) { x.fly(); }

public static void w(ActionCharacter x) { x.fight(); }

public static void main(String[] args) {

Hero h = new Hero();

t(h); // Используем объект в качестве типа CanFight

u(h); // Используем объект в качестве типа CanSwim

v(h); // Используем объект в качестве типа CanFly

w(h); // Используем объект в качестве ActionCharacter

 

}

}

Мы видим, что класс Неrо сочетает реальный класс ActionCharacter с интерфейсами CanFight, CanSwim и CanFly. При объединении реального класса с интерфейсами на первом месте должен стоять реальный класс, а за ним следуют ин­терфейсы (иначе компилятор выдаст ошибку).

Заметьте, что объявление метода fight() в интерфейсе CanFight совпадает с тем, что имеется в классе ActionCharacter, и поэтому в классе Неrо нет определения метода fight().

Интерфейсы можно расширять, но при этом получается другой интерфейс. Необходимым условием для создания объектов нового типа является наличие всех определений. Хотя класс Неrо не имеет явного определения метода fight(), это определение существует в классеActionCharacter, что и делает возможным создание объектов класса Неrо.

Класс Adventure содержит четыре метода, которые принимают в качестве аргументов разнообразные интерфейсы и реальный класс. Созданный объект Неrо передается всем этим методам, а это значит, что выполняется восходящее пре­образование объекта к каждому интерфейсу по очереди. Система интерфейсов Java спроектирована так, что она нормально работает без особых усилий со стороны программиста.

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

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

 

Расширение интерфейса через наследование

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

//: interfaces/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 VeryBadVampire implements Vampire {

public void menace() {}

public void destroy() {}

public void kill() {}

public void drinkBlood() {}

}

 

public class HorrorShow {

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

static void v(DangerousMonster d) {

d.menace();

d.destroy();

}

static void w(Lethal l) { l.kill(); }

public static void main(String[] args) {

DangerousMonster barney = new DragonZilla();

u(barney);

v(barney);

Vampire vlad = new VeryBadVampire();

u(vlad);

v(vlad);

w(vlad);

}

}

DangerousMonster представляет собой простое расширение Monster, в результате которого образуется новый интерфейс. Он реализуется классом DragonZilla.

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

 

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

При реализации нескольких интерфейсов может возникнуть небольшая проблема. В только что рассмотренном примере интерфейс CanFight и класс ActionCharacter имеют идентичные методы void fight(). Хорошо, если методы полностью тождественны, но что, если они различаются по сигнатуре или типу возвращаемого значения? Рассмотрим такой пример:

//: interfaces/InterfaceCollision.java

package interfaces;

 

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 {}

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

InterfaceCollіsion.java.23 f() в C не может реализовать f() в I1; попытка использовать несовместимые возвращаемые типы обнаружено: int требуется- void



<== предыдущая лекция | следующая лекция ==>
Поведение полиморфных методов при вызове из конструкторов | InterfaceCollіsi on java:24- интерфейсы І1 и I1 несовместимы; оба определяют f(), но с различными возвращаемыми типами


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


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

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

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


 


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

 
 

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

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