Java. Абстрактные, виртуальные, конечные и статические методы.



Статические методы и поля

Для того чтобы не надо было создавать объект класса Math (и других аналогичных классов) каждый раз, когда надо вызвать sin (и другие подобные функции), введено понятие статических методов (static method; иногда в русском языке они называются статичными). Статический метод (отмечаемый словом static в описании) можно вызвать, не создавая объекта его класса. Поэтому можно писать

double x = Math.sin(1);

вместо

 

Math m = new Math(); double x = m.sin(1);

Ограничение, накладываемое на статические методы, заключается в том, что в объекте this они могут обращаться только к статическим полям и методам. Статические поля имеют тот же смысл, что и в C++: каждое существует только в одном экземпляре.

 

Абстрактность

В Java все методы являются виртуальными в терминологии C++: при вызове метода, по-разному определённого в базовом и наследующем классах, всегда производится проверка времени выполнения. Абстрактным методом (описатель abstract) в Java называется метод, для которого заданы параметры и тип возвращаемого значения, но не тело. Абстрактный метод определяется в классах-наследниках. В C++ то же самое называется чисто виртуальной функцией. Для того чтобы в классе можно было описывать абстрактные методы, сам класс тоже должен быть описан как абстрактный. Объекты абстрактного класса создавать нельзя.

При описании класса Pet мы не можем задать в методе voice () никакой полезный алгоритм, поскольку у всех животных совершенно разные голоса.

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

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

Как же использовать абстрактные классы? Только порождая от них подклассы, в которых переопределены абстрактные методы.

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

Хотя элементы массива singer [] ссылаются на подклассы Dog, Cat, Cow , но все-таки это переменные типа Pet и ссылаться они могут только на поля и методы, описанные в суперклассе Pet . Дополнительные поля подкласса для них недоступны. Попробуйте обратиться, например, к полю k класса Dog , написав singer [0].k . Компилятор "скажет", что он не может реализовать такую ссылку. Поэтому метод, который реализуется в нескольких подклассах, приходится выносить в суперкласс, а если там его нельзя реализовать, то объявить абстрактным. Таким образом, абстрактные классы группируются на вершине иерархии классов.

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

void voice(){}

Получится полноценный метод. Но это искусственное решение, запутывающее структуру класса.

Замкнуть же иерархию можно окончательными классами.

В языке Java определение метода включает его объявление и реализацию. Определение метода всегда указывается в теле класса.

Метод может иметь модификаторы доступа, возвращать значение и получать параметры.

Метод может иметь следующие модификаторы:

public, protected или private -модификаторы доступа;

static - модификатор метода класса.

abstract, final, native или synchronized.

Для модификаторов доступа метода определены следующие правила:

public указывает, что данный метод будет доступен везде, где доступен класс, в котором он определен;

protected указывает, что данный метод будет доступен как внутри пакета, содержащего объявление класса, в котором он определен, так и внутри любого подкласса данного класса;

private указывает, что данный метод не будет доступен нигде кроме тела класса, в котором он определен.

По умолчанию метод считается доступным везде внутри пакета, содержащего класс, в котором он определен, и недоступным ни в каком другом подклассе указанного класса в том случае, если подкласс содержится в другом пакете.

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

Метод, объявленный с модификатором static, называется статическим методом (или методом класса) и может быть вызван без создания экземпляра класса. Этот метод всегда вызывается непосредственно из класса. Статический метод имеет доступ к другим статическим переменным и методам данного класса.

Если статический метод определен как final-метод, то он не может быть переопределен.

Например:

// Файл A.java

package classa;

public class A implements B {

public A() { }

static int b=1;

public int Metod1(){return a;}

public static int Metod2(){

return 0;} //Статический метод

}

interface B {

final public static int a=1; 

// Статическая переменная

int Metod1();

}

 

// Файл C.java

package classa;

public class C extends A {

public C() { }

static int b=3;

public int Metod1(){return a;}

public static int Metod2(){return 77;}

public static void main(String[] args) {

   System.out.println(A.Metod2());

   System.out.println(C.Metod2());

}

}

При переопределении методов их модификаторы доступа должны совпадать. Так, нельзя переопределить метод, имеющий модификатор доступа public, методом с модификатором доступа private.

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

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

Например:

public class A extends AA implements B {

public A() { }

public int Metod1(){return 0;}

public static int Metod2(){return 0;}

int Metod3(){return 0;}

public int Metod4(){return 0;}

}

interface B {

int Metod1();

abstract int Metod4();

}

 abstract class AA{

abstract int Metod3();

 }

Методы, объявленные с модификатором private, не могут быть абстрактными методами, так как они недоступны вне тела класса. Статические методы также не могут выступать в качестве абстрактных методов, так как считаются конечными и не могут быть переопределены.

Объявление метода с модификатором final запрещает его последующее переопределение. Такие методы называются конечными методами, и по умолчанию считается, что private-метод всегда является конечным методом.

Методы, объявленные с модификатором native, могут иметь реализацию на другом языке программирования. Эти методы используются для написания машинно-зависимого кода. native-методы в Java-программе не имеют тела метода.

Synchronized-методы выполняются с блокировкой:

для метода класса выполняется блокировка класса,

для метода экземпляра - блокировка объекта.

Это позволяет предотвратить параллельный доступ к данным из различных потоков в многопоточном приложении.

Метод может возвращать значение заданного типа. В этом случае:

перед именем метода указывается тип возвращаемого значения;

в теле метода присутствует оператор return, определяющий возвращаемое значение.

Если метод не имеет возвращаемого значения, то он должен быть объявлен с ключевым словом void, указывающим на отсутствие возвращаемого значения.

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


C# и Java. Интерфейсы.

 Интерфейс определяет набор методов, которые будут реализованы классом. Сам

интерфейс не реализует методы. Таким образом, интерфейс — это логическая конст-

рукция, которая описывает методы, не устанавливая жестко способ их реализации. В

этой главе рассматриваются еще два типа данных С#: структуры и перечисления.

Структуры подобны классам, за исключением того, что они обрабатываются как типы

значений, а не как ссылочные типы. Перечисления — это списки именованных цело-

численных констант. Структуры и перечисления вносят существенный вклад в общую

копилку средств и инструментов, составляющих среду программирования С#.

Интерфейсы

В объектно-ориентированном программировании иногда требуется определить,

что класс должен делать, а не как он будет это делать. Вы уже видели такой подход на

примере абстрактного метода. Абстрактный метод определяет сигнатуру для метода,

но не обеспечивает его реализации. В производном классе каждый абстрактный ме-

тод, определенный базовым классом, реализуется по-своему. Таким образом, абст-

рактный метод задает интерфейс для метода, но не способ его реализации. Несмотря

на всю полезность абстрактных классов и методов, эту идею можно развить. В С#

предусмотрена возможность полностью отделить интерфейс класса от его реализации

с помощью ключевого слова interface.

Интерфейсы синтаксически подобны абстрактным классам. Однако в интерфейсе

ни один метод не может включать тело, т.е. интерфейс в принципе не предусматрива-

ет какой бы то ни было реализации. Он определяет, что должно быть сделано, но не

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

чество классов. При этом один класс может реализовать любое число интерфейсов.

Для реализации интерфейса класс должен обеспечить теля (способы реализации)

методов, описанных в интерфейсе. Каждый класс может определить собственную реа-

лизацию. Таким образом, два класса могут реализовать один и тот же интерфейс раз-

личными способами, но все классы поддерживают одинаковый набор методов. Следо-

вательно, код, "осведомленный" о наличии интерфейса, может использовать объекты

любого класса, поскольку интерфейс для всех объектов одинаков. Предоставляя про-

граммистам возможность применения такого средства программирования, как интер-

фейс, С# позволяет в полной мере использовать аспект полиморфизма, выражаемый

как "один интерфейс — много методов".

Интерфейсы объявляются с помощью ключевого слова i n t e r f a c e . Вот как выгля-

дит упрощенная форма объявления интерфейса:

i n t e r f a c e имя{

тип_возврата имя_метода1 {список_параметров) ;

тип_возврата имя_метода2 {список_параметров) ;

// . . .

тип_возврата имя_методаЫ(список_параметров) ;

}

Имя интерфейса задается элементом имя. Методы объявляются с использованием

лишь типа возвращаемого ими значения и сигнатуры. Все эти методы, по сути, — аб-

страктные. Как упоминалось выше, для методов в интерфейсе не предусмотрены спо-

собы реализации. Следовательно, каждый класс, который включает интерфейс, дол-

жен реализовать все его методы. В интерфейсе методы неявно являются открытыми

(public-методами), при этом не разрешается явным образом указывать спецификатор

доступа.

Рассмотрим пример интерфейса для класса, который генерирует ряд чисел.

public interface ISeries {

int getNext(); // Возвращает следующее число ряда,

void reset (); // Выполняет перезапуск,

void setStart(int x) ; // Устанавливает начальное

// значение.

Этот интерфейс имеет имя ISeries. Хотя префикс " I " необязателен, • многие

программисты его используют, чтобы отличать интерфейсы от классов. Интерфейс

ISeries объявлен открытым, поэтому он может быть реализован любым классом в

любой программе.

Помимо сигнатур методов интерфейсы могут объявлять сигнатуры свойств, индек-

саторов и событий. События рассматриваются в главе 15, поэтому здесь мы остано-

вимся на методах, индексаторах и свойствах. Интерфейсы не могут иметь членов дан-

ных. Они не могут определять конструкторы, деструкторы или операторные методы.

Кроме того, ни один член интерфейса не может быть объявлен статическим.

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

Итак, если интерфейс определен, один или несколько классов могут его реализо-

вать. Чтобы реализовать интерфейс, нужно указать его имя после имени класса по-

добно тому, как при создании производного указывается базовый класс. Формат запи-

си класса, который реализует интерфейс, таков:

class имя_класса : имя__интерфейса {

// тело класса

Нетрудно догадаться, что имя реализуемого интерфейса задается с помощью эле-

мента имя_интерфейса.

Если класс реализует интерфейс, он должен это сделать в полном объеме, т.е. реа-

лизация интерфейса не может быть выполнена частично.

Классы могут реализовать несколько интерфейсов. В этом случае имена интерфей-

сов отделяются запятыми. Класс может наследовать базовый класс и реализовать один

или несколько интерфейсов. В этом случае список интерфейсов должно возглавлять

имя базового класса.

Методы, которые реализуют интерфейс, должны быть объявлены открытыми. Дело

в том, что методы внутри интерфейса неявно объявляются открытыми, поэтому их

реализации также должны быть открытыми. Кроме того, сигнатура типа в реализации

метода должна в точности совпадать с сигнатурой типа, заданной в определении ин-

терфейса.

Рассмотрим пример реализации интерфейса ISeries, объявление которого приве-

дено выше. Здесь создается класс с именем ByTwos, генерирующий ряд чисел, в кото-

эом каждое следующее число больше предыдущего на два.

// Реализация интерфейса ISeries,

class ByTwos : ISeries {

int start;

int val;

public ByTwos() {

start = 0;

val = 0;

public int getNext() {

val += 2;

return val;

public void reset() {

val = start;

public void setStart(int x) {

start = x;

val = start;

Как видите, класс ByTwos реализует все три метода, определенные интерфейсом

ISeries. Иначе и быть не может, поскольку классу не разрешается создавать частич-

ную реализацию интерфейса.

Рассмотрим пример, демонстрирующий использование класса ByTwos. Вот его код:

// Демонстрация использования интерфейса,

// реализованного классом ByTwos.

using System;

class SeriesDemo {

public static void Main() {

ByTwos ob = new ByTwos() ;

for(int i=0; i < 5;

Console.WriteLine("Следующее значение равно " +

ob.getNext());

Console.WriteLine("ХпПереход в исходное состояние.")

ob.reset ();

for(int i=0; i < 5; i++)

Console.WriteLine("Следующее значение равно " +

ob.getNext());

Console.WriteLine("ХпНачинаем с числа 100.");

ob.setStart (100);

for(int i=0; i < 5; i++)

Console.WriteLine("Следующее значение равно " +"

ob.getNext());

Чтобы скомпилировать программу SeriesDemo, необходимо включить в процесс

компиляции файлы, которые содержат классы ISeries, ByTwos и SeriesDemo. Для

создания выполняемой программы компилятор автоматически скомпилирует все три

файла. Если эти файлы называются, например, ISeries, cs, ByTwos. cs и

SeriesDemo.es, то программа скомпилируется посредством выполнения такой ко-

мандной строки:

I >csc SeriesDemo.cs ISeries.cs ByTwos.cs

В классах, которые реализуют интерфейсы, можно определять дополнительные

члены. Например, в представленную ниже версию класса ByTwos добавлен метод

getPrevious (), который возвращает предыдущее значение ряда.

// Реализация интерфейса ISeries с дополнительно

// определяемым методом getPrevious().

class ByTwos : ISeries {

int start;

int val;

int prev;

public ByTwos() {

start = 0;

val = 0;

prev = -2;

}

public int getNextO {

prev = val;

val += 2;

return val;

}

public void reset() {

val = start;

prev = start - 2;

}

public void setStart(int x) {

start = x;

val = start;

prev = val - 2;

}

// Метод, не объявленный в интерфейсе ISeries,

public int getPrevious() {

return prev;

Обратите внимание на то, что добавление метода getPrevious () потребовало

внесения изменений в реализацию методов, определенных интерфейсом ISeries. Но

поскольку интерфейс для этих методов остается прежним, при изменении не разру-

шается код, написанный ранее. В этом и заключается одно из достоинств использова-

ния интерфейсов.

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

Рассмотрим, например, класс Primes, который генерирует ряд простых чисел. Обра-

тите внимание на то, что его способ реализации интерфейса ISeries в корне отлича-

ется от используемого классом ByTwos.

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

// ряда простых чисел.

class Primes : ISeries {

int start;

int val;

public Primes () {

start = 2;

val = 2;

}

public int getNext() {

int i, j;

bool isprime;

for(i = val; i < 1000000;

isprime = true;

for(j = 2; j < (i/j + 1)

if((i%j)==0) {

isprime = false;

break;

if(isprime) {

val = i;

break;

return val;

public void reset() {

val = start;

public void setStart(int x) {

start = x;

val = start;

Здесь важно понимать, что, хотя классы Primes и ByTwos генерируют разные ряды

чисел, оба они реализуют один и тот же интерфейс ISeries. И в этом нет ничего

удивительного, поскольку каждый класс волен решить эту задачу так, как "считает"

нужным.

 

Интерфейсы Java

Объявление интерфейса вводит новый ссылочный тип, членами которого являются константы и абстрактные методы. Этот тип не может быть реализован, но его могут реализовывать несвязанные классы, обеспечивая реализацию его абстрактных методов. Программы на Яве могут использовать интерфейсы для того, чтобы упускать для связанных классов разделение общего абстрактного суперкласса или для добавления методов в Object. Интерфейс может быть объявлен, для того чтобы быть прямымрасширением одного или более интерфейсов, предполагая, что он неявно определяет все абстрактные методы и константы расширяющих его интерфейсов, кроме каких-нибудь констант, которые могут быть скрыты. Класс может быть объявлен для того, чтобы непосредственно реализовать один или более интерфейсов, в том смысле, что любой экземпляр класса реализует все абстрактные методы заданные интерфейсом или интерфейсами. Класс обязательно реализует все интерфейсы, что и его непосредственные суперклассы и непосредственные суперинтерфейсы. Это (множественное) наследование интерфейса позволяет объектам поддерживать (множественное) общее поведение без разделения реализации. Переменная, чей объявленный тип - это интерфейсный тип, может иметь в качестве значения ссылку на какой-нибудь объект, который является экземпляром класса, объявленного для реализации указанного интерфейса. Этого не достаточно для того, чтобы класс производил реализацию всех абстрактных методов интерфейса; класс или один из его суперклассов должен фактически быть объявлен, чтобы реализовать интерфейс, или иначе класс не учитывается при реализации интерфейса. 9.1 Объявление интерфейса Объявление интерфейса определяет новый ссылочный тип: InterfaceDeclaration:        InterfaceModifiersopt interface Identifier                    ExtendsInterfacesopt InterfaceBody   Ошибка времени компиляции происходит, если Identifier, являющийся именем интерфейса появляется как имя какого-нибудь другого класса или интерфейса в том же самом пакете. Также ошибка времени компиляции происходит, если Identifier, являющийся именем интерфейса появляется как имя, под которым класс или интерфейс должен быть известен через объявление одиночного импорта типа в модуле компиляции содержащем объявление интерфейса. В примере:   class Point { int x, y; }   interface Point { void move(int dx, int dy); } ошибка времени компиляции происходит, потому что class и interface в одном и том же пакете не могут иметь одинаковые имена. 9.1.1 Область действия имени интерфейсного типа Identifier определяет имя интерфейса областью действия которого является весь пакет, в котором он объявлен. Это правило схоже с правилом для имен классового типа; смотрите пример, содержащий классы. 9.1.2 Модификаторы интерфейса Объявлению интерфейса может предшествовать объявление модификаторов: InterfaceModifiers:        InterfaceModifier        InterfaceModifiers InterfaceModifier   InterfaceModifier: один из        public abstract   Если один и тот же модификатор появляется более одного раза в объявлении интерфейса, то происходит ошибка времени компиляции. 9.1.2.1 Абстрактные интерфейсы Каждый интерфейс по умолчанию является abstract (абстрактным). Этот модификатор устарел и не используется в новых программах на Яве. 9.1.3 Суперинтерфейсы Если предусмотрено предложение extends, тогда объявляемый интерфейс расширяет каждый из перечисленных интерфейсов и поэтому наследует методы и константы каждого из названных интерфейсов. Эти указанные интерфейсы есть прямые суперинтерфейсы объявляемого интерфейса. Любой класс, который реализует объявленный интерфейс, также обеспечивает реализацию всех интерфейсов, которые расширяют интерфейс и которые доступны для класса. ExtendsInterfaces:        extends InterfaceType        ExtendsInterfaces , InterfaceType Для большей ясности, следующее повторяется из §4.3: InterfaceType:        TypeName Каждый InterfaceType в предложении extends из описания интерфейса должен именовать доступный интерфейсный тип; иначе происходит ошибка времени компиляции. Ошибка времени компиляции происходит, если имеется рекурсия такая что интерфейс непосредственно или косвенно расширяет себя. Нет никакого аналога классу Object для интерфейсов; то есть, в то время как каждый класс - расширение класса Object, нет никакого отдельного интерфейса в котором все интерфейсы являются расширениями. Отношение суперинтерфейса - переходное смыкание отношения прямого суперинтерфейса. Интерфейс K - суперинтерфейс интерфейса I, если следующее является верным: K- непосредственный суперинтерфейс I. Cуществует интерфейс J такой, что K является суперинтерфейсом J, и J- суперинтерфейс I, при рекурсивном применении этого определения. Интерфейс I, как говорят, является подинтерфейсом интерфейса K, когда K - суперинтерфейс I. 9.1.4 Тело интерфейса и объявление членов Тело интерфейса может объявлять члены интерфейса: InterfaceBody:        { InterfaceMemberDeclarationsopt }   InterfaceMemberDeclarations:        InterfaceMemberDeclaration        InterfaceMemberDeclarations InterfaceMemberDeclaration   InterfaceMemberDeclaration:        ConstantDeclaration        AbstractMethodDeclaration Область действия члена, объявленного в типе интерфейса - все тело объявленного интерфейса. 9.1.5 Доступ к именам членов интерфейса Все члены интерфейса по умолчанию public. Они доступны вне пакета, где интерфейс объявлен, если интерфейс также объявлен как public и пакет, содержащий интерфейс - доступен, как описывается в . 9.2 Члены интерфейса Члены интерфейса - это члены, которые унаследованы от прямого суперинтерфейса и члены, которые объявлены в интерфейсе. Интерфейс наследует из интерфейсов расширения, все члены этих интерфейсов, кроме полей, которые скрываются и методов которые игнорируются. 9.3 Объявление (константного) поля ConstantDeclaration:        ConstantModifiers Type VariableDeclarator   ConstantModifiers: one of        public static final Каждое объявление поля в теле интерфейса поумолчанию public, static и final. Это разрешено,но рекомендуется как соответствие со стилем,резервно определять любой или все этимодификаторы для таких полей. Объявление константы в интерфейсе не должно включать никакой из модификаторов -synchronized, transient, или volatile, иначе происходит ошибка времени компиляции . Это возможно для интерфейса, чтобы унаследовать больше чем одно поле с одним и тем же именем (§8.3.3.3). Такая ситуация сама по себе не вызывает ошибки времени компиляции. Однако, любая попытка в пределах тела интерфейса обратиться к некоторому полю при помощи простого имени окончится ошибкой времени компиляции, потому что такая ссылка неоднозначна. Могло бы существовать несколько путей, которыми одно и то же объявление поля могло бы наследоваться от интерфейса. В такой ситуации, поле рассматривается как унаследованное только один раз, и это может быть обращено с помощью простого имени без двусмысленности. 9.3.1 Инициализация полей в интерфейсах Каждое поле в теле интерфейса должно иметь выражение инициализации, которое не должно быть константой. Инициализатор -переменная определена и присваивание, выполняется только один раз, когда интерфейс - инициализирован (§12.4). Ошибка времени компиляции происходит, если инициализация выражения поля интерфейса содержит ссылку на простое имя того же поля или другого поля, текстовое объявление которого в том же самом интерфейсе происходит позже. Таким образом:   interface Test {        float f = j;        int j = 1;        int k = k+1; } } происходят две ошибки времени компиляции, потому что j упомянута в инициализации f прежде j объявлен и потому что инициализация k обращается к k. (Одна тонкость состоит в том, что, во время выполнения, fields, которые инициализируются постоянными значениями во время компиляции - инициализируются первыми. Это также применяется к полям static final в классах (§8.3.2.1). Это означает, в частности, что эти поля никогда не будут иметь их начальные значения по умолчанию (§4.5.4). Для более углубленного изучения см. § 12.4.2 и § 13.4.8.) Если ключевое слово this (§ 15.7.2) или ключевое слово super (§ 15.10.2, § 15.11) происходит в инициализации выражения для поля интерфейса, тогда происходит ошибка времени компиляции. 9.4.1 Наследование и замещение Если интерфейс объявляет метод, то объявление этого метода как говорят замещает все методы с той же самой сигнатурой в суперинтерфейсах интерфейса, который был бы иначе доступны коду в этом интерфейсе. Если метод объявлен в интерфейсе замещает объявление метода в другом интерфейсе, произойдет ошибка времени компиляции если методы имеют различные типы возврата или если один имеет тип возврата, а другой - void. Кроме того, объявление метода не должно иметь предложения throws которое конфликтует с любыми из тех методов, которые замещаются; иначе, произойдет ошибка времени компиляции. Методы замещаются в соответствии с сигнатурами.Если, например, интерфейс объявляет два метода public с одним и тем же именем, и подинтерфейс замещает один из них, то подинтерфейс все еще наследует другой метод. Интерфейс наследует от его непосредственных суперинтерфейсов все методы суперинтерфейсов, которые - не замещаются объявлением в интерфейсе. Это возможно для интерфейса, чтобы унаследовать больше чем один метод с одной и той же сигнатурой. Такая ситуация сама по себе не вызывает ошибки времени компиляции. Интерфейс унаследует все методы. Однако, произойдет ошибка времени компиляции если, для любых двух таких унаследованных методов, или они имеют различные типы возврата, или один имеет тип возврата, а другой - void. (В этом случае предложение throws не генерирует ошибки.) Существует несколько путей, которыми одно и тоже объявление метода наследуется от интерфейса. Это не вызывает никаких трудностей и никогда не приводит к ошибке при компиляции. 9.4.2 Перегрузка Если два метода интерфейса (или оба объявлены в одном и том же интерфейсе, или оба наследованы интерфейсом, или один объявлен и один унаследован) имеют одно и то же имя, но различные сигнатуры, то имя метода, как говорят, является игнорированным. Это не вызывает никаких трудностей и никогда не приводит к ошибке времени компиляции. Типы возвращаемых значений и предложения trows двух методов с одинаковым именем и разнымисигнатурами не имеют друг к другу никакого отношения. 9.4.3 Примеры объявления абстрактных методов Следующие примеры иллюстрируют некоторый (возможно тонкие) пункты относительно объявлений абстрактных методов. 9.4.3.1 Пример: замещения Методы, объявленные в интерфейсах - abstract и таким образом не содержат реализацию. Относительно того, что может быть выполнено замещением объявления метода, подтверждает сигнатура метода, что должны быть ограничены исключения, которые могли бы быть брошен реализацией метода. Кроме задания сигнатуры в объявлении замещающего метода указываются исключения, которые могут быть сгенерированы реализацией метода. Здесь вариант примера, показанного в §8.4.3.1:   class BufferEmpty extends Exception {        BufferEmpty() { super(); }        BufferEmpty(String s) { super(s); } } class BufferError extends Exception {        BufferError() { super(); }        BufferError(String s) { super(s); } } public interface Buffer {        char get() throws BufferEmpty, BufferError; } public interface InfiniteBuffer extends Buffer {         char get() throws BufferError;                                                                                                                                 // override } 9.4.3.2 Пример: Перегрузка В коде примера: interface PointInterface {        void move(int dx, int dy); }     interface RealPointInterface extends PointInterface {        void move(float dx, float dy);        void move(double dx, double dy); } метод move игнорируется в интерфейсе RealPointInterface с тремя различными сигнатурами, две из них объявлены и одна унаследована. Любой класс, который реализует интерфейс RealPointInterface, должен обеспечить реализацию всех трех сигнатур метода.

 


C#. Структуры.

Структура (structure) – тип-значение

· экземпляр размещается в стеке или непосредственно в другом экземпляре

·  поля экземпляра размещаются в самом экземпляре

·  переменная — не указатель

· нет разыменования

·  не выполняется сборка мусора

·  при присваивании выполняется копирование

Синтаксис

· Объявление

struct ИмяСтруктуры : Интерфейсы

{

// тело структуры

}

· Создание

ИмяСтруктуры имяПеременной = new ИмяСтруктуры();

ИмяСтруктуры имяПеременной;

Правила

· неявно наследуются от System.ValueType (наследник System.Object); изменить базовый тип нельзя

·  неявно объявлены как sealed

·  могут реализовывать интерфейсы (пользоваться следует осторожно)

·  не могут содержать конструктор по умолчанию и деструктор

·  неприменимы модификаторы abstract, virtual, protected

Пример

struct Point {

private double _x;

private double _y;

public double X { ... }

public double Y { ... }

public void Offset(double x, double y)

{ ... }

}

Point p = new Point();

p.X = -1;

p.Y = 10;

Console.WriteLine(p);

void Console.WriteLine(object value)

Использование структур

· Главная обязанность – хранение данных

·  Тип ведет себя подобно элементарному

·  Тип не будет наследоваться

·  У типа не будет производных типов

·  Тип не будет полиморфным

·  Размер экземпляров мал (16 байт)

·  Экземпляры не передаются в качестве параметров и не возвращаются из метода

Неизменяемые структуры

· Представляют неизменяемые атомарные значения

·  Все поля – readonly

·  У всех свойств – только аксессор get

·  Набор перегруженных конструкторов

Структуры определяются с помощью ключевого слова struct, например:

Язык C#

public struct PostalAddress

{

// Fields, properties, methods and events go here...

}

Структуры используют большую часть того же синтаксиса, что и классы, однако они более ограничены по сравнению с ними.

В объявлении структуры поля не могут быть инициализированы до тех пор, пока они будут объявлены как постоянные или статические.

Структура может не объявлять используемый по умолчанию конструктор (конструктор без параметров) или деструктор.

Структуры не могут наследовать от классов или других структур.

Структуры копируются при присваивании. При присваивании структуры к новой переменной выполняется копирование всех данных, а любое изменение новой копии не влияет на данные в исходной копии.

Структуры являются типами значений, а классы — ссылочными типами.

В отличие то классов, структуры могут быть созданы без использования нового оператора.

Структуры могут объявлять конструкторы, имеющие параметры.

Структура не может быть унаследованной от другой структуры или класса и не может быть основой для других классов. Все структуры наследуют непосредственно от System.ValueType, который наследует от System.Object.

Структуры могут реализовывать интерфейсы.

Структура может использоваться как тип, допускающий значение NULL, и ей можно назначить значение NULL.


 

C#. Перечисления.

Понятие и синтаксис

Перечисление (enumeration, enumerated type) – множество именованных целочисленных констант

enum Имя : ОсновнойТип {

Константа = значение,

...

}

Правила

· неявно наследуются от System.Enum (наследник System.ValueType); изменить базовый тип нельзя

·  каждая константа — целое число

·  неявное преобразование к целому типу не определено

·  неприменимы модификаторы abstract, virtual, protected

Пример

enum Color : byte {

None = 0,

Red,

Green = 3,

Blue

}

Color color = Color.Green;

На самом деле

struct Color : System.Enum {

public const Color None = (Color)0,

public const Color Red = (Color)1,

public const Color Green = (Color)3,

public const Color Blue = (Color)4

public Byte value__;

}

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

Обычно лучше всего определять перечисление прямо в пространстве имен, чтобы всем классам в пространстве имен было одинаково удобно получить к нему доступ. Однако перечисление также может быть вложено в классе или структуре.

По умолчанию первому перечислителю задано значение, равное нулю, а значение каждого последующего оператора увеличивается на 1. Например:

enum Days {Sat, Sun, Mon, Tue, Wed, Thu, Fri};

В этом перечислении Sat имеет значение 0, Sun — 1, Mon — 2 и т. д. Для переопределения значений по умолчанию перечисления могут иметь инициализаторы. Пример.

enum Days {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};

 

В этом перечислении последовательность элементов принудительно начинается с 1, а не с 0. Однако настоятельно рекомендуется, чтобы перечисление содержало константу со значением 0. Дополнительные сведения см. в разделе Типы перечислений (руководство по программированию в C#).

Каждый тип перечисления имеет базовый тип, который может быть любым целым типом, исключая char. По умолчанию базовым типом элементов перечисления является int. Чтобы объявить перечисление другого целого типа, например byte, после идентификатора поставьте двоеточие, а затем укажите тип.

enum Days : byte {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};

Для перечисления утверждены следующие типы: byte, sbyte, short, ushort, int, uint, long или ulong.

Переменной типа Days может быть назначено любое значение, входящее в диапазон базового типа; значения не ограничены именованными константами.

Значением по умолчанию enum E является значение, созданное выражением (E)0.Примечание.

Имя перечислителя может не содержать пробелов.

Базовый тип указывает размер хранилища, выделенного для каждого перечислителя. Однако для преобразования из типа enum в целочисленный тип необходимо явное приведение. Например, следующий оператор назначает перечислитель Sun переменной типа int, используя приведение для преобразования enum в int:

int x = (int)Days.Sun;

При применении System.FlagsAttribute к перечислению, содержащему несколько элементов в сочетании с битовой операцией OR, будет заметно, что атрибут оказывает влияние на действие enum, когда оно используется с некоторыми инструментами. Эти изменения можно заметить при использовании таких средств, как методы класса Console, вычислитель выражений и т. д.


Дата добавления: 2018-05-13; просмотров: 1189; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!