В.В. Кулямин - Технологии программирования. Компонентный подход (1134162), страница 51
Текст из файла (страница 51)
Говоря по-другому, предусловие операции при перегрузкене должно усиливаться.• Множество ситуаций, в которых система в целом может оказаться после вызова операции внаследнике, должно быть подмножеством набора ситуаций, в которых она может оказатьсяв результате вызова этой операции в предке. То есть, постусловие операции при перегрузкене должно ослабляться.Статические операции, относящиеся к классу в целом, а не к его объектам, не виртуальны.
Онине могут быть перегружены, но могут быть перекрыты, если в потомке определяются статическиеоперации с такими же сигнатурами.В Java все нестатические методы классовявляются виртуальными, т.е. перегружаютсяпри определении метода с такой же сигнатуройв классе-потомке.Но в Java, в отличие от C#, можно вызыватьстатические методы и обращаться кстатическим полям класса через ссылки на егообъекты (в том числе, и через this). Поэтомуработу невиртуальных методов можносмоделировать с помощью обращений кстатическим операциям.В C# нестатические операции (обычные методыи методы доступа к свойствам, индексерам исобытиям) могут быть как виртуальными, т.е.перегружаемыми, так и невиртуальными.Для декларации виртуального метода (свойства,индексера или события) необходимо пометитьего модификатором virtual.
Метод (свойство,индексер, событие), перегружающий его вклассе-потомке надо в этом случае пометитьмодификатором override. Если же мы не хотимперегружать элемент предка, а хотимопределить новый элемент с такой жесигнатурой (т.е. перекрыть старый), то его надопометить модификатором new.Элемент, не помеченный как virtual, неявляется перегружаемыми — его можно толькоперекрыть.
При вызове операции с такойсигнатурой в некотором объекте будет вызванаее реализация, определяемая подекларированному типу объекта.Приводимые ниже примеры на обоих языках иллюстрируют разницу в работе виртуальных иневиртуальных операций.using System;class A{public void m()class A{public virtual void m()175{}}{System.out.println("A.m() called");public static void n(){System.out.println("A.n() called");}}}Console.WriteLine("A.m() called");public void n(){Console.WriteLine("A.n() called");}class B extends A{public void m(){System.out.println("B.m() called");}class B : A{public override void m(){Console.WriteLine("B.m() called");}public static void n(){System.out.println("B.n() called");}public new void n(){Console.WriteLine("B.n() called");}}public class C{public static void main(String[] args){A a = new A();B b = new B();A c = new B();}}}public class C{public static void Main(){A a = new A();B b = new B();A c = new B();a.m();b.m();c.m();System.out.println("-----");a.n();b.n();c.n();}Представленный в примере код выдаетследующие результаты.A.m()B.m()B.m()----A.n()B.n()A.n()calledcalledcalledcalledcalledcalled}a.m();b.m();c.m();Console.WriteLine("-----");a.n();b.n();c.n();Если в приведенном примере убратьмодификатор new у метода n() в классе B,ошибки компиляции не будет, но будет выданопредупреждение о перекрытии имен, возможнослучайном.Представленный в примере код выдаетследующие результаты.A.m()B.m()B.m()----A.n()B.n()A.n()calledcalledcalledcalledcalledcalledЭлементы типовЭлементы или члены (members) пользовательских типов могут быть методами, полями (вклассах) и вложенными типами.
В классе можно также объявлять конструкторы, служащие длясоздания объектов этого класса. В обоих языках описание конструктора похоже на описаниеметода, только тип результата не указывается, а вместо имени метода используется имя самогокласса.176В обоих языках поля можно только перекрывать в наследниках, а методы можно иперегружать. Вложенные типы, как и поля, могут быть перекрыты.У каждого элемента класса могут присутствовать модификаторы, определяющие доступностьэтого элемента из разных мест программы, а также его контекст — относится ли он к объектамэтого класса (нестатический элемент) или к самому классу (статический элемент, помечается какstatic).Для указания доступности в обоих языках могут использоваться модификаторы public,protected и private, указывающие, соответственно, что данный элемент доступен везде, гдедоступен содержащий его тип, доступен только в описаниях типов-наследников содержащеготипа, или только в рамках описания самого содержащего типа.
Доступность по умолчанию, безуказания модификатора трактуется в рассматриваемых языках различно.Нестатические методы в обоих языках (а также свойства, индексированные свойства и событияв C#) могут быть объявлены абстрактными (abstract), т.е. не задающими реализациисоответствующей операции. Такие методы (а также свойства, индексированные свойства исобытия в C#) помечаются модификатором abstract. Вместо кода у абстрактного метода сразупосле описания полной сигнатуры идет точка с запятой.Методы (свойства, индексированные свойства и события в C#), которые не должны бытьперегружены в наследниках содержащего их класса, помечаются в Java как final, а в C# какsealed.В обоих языках можно использовать операции, реализованные на других языках. Для этого вC# используются стандартные механизмы .NET — класс реализуется на одном из языков,поддерживаемых .NET, с учетом ограничений на общие библиотеки этой среды и становитсядоступен из любого другого кода на поддерживаемом .NET языке.В Java для этого предусмотрен механизм Java Native Interface, JNI [6,7].
Класс Java можетиметь ряд внешних методов, помеченных модификатором native. Вместо кода у таких методовсразу после описания полной сигнатуры идет точка с запятой. Они по определенным правиламреализуются в виде функций на языке C (или на другом языке, если можно в результатекомпиляции получить библиотеку с интерфейсом на C).
Внешние методы, а также свойства,индексированные свойства, события и операторы, привязываемые по определенным правилам кфункциям, которые имеют интерфейс на C и не вложены в среду .NET, есть и в C# — там такиеоперации помечаются как extern.В Java, помимо перечисленных членов типов,имеются инициализаторы. Их описаниеприведено ниже.Инициализаторы относятся только к томуклассу, в котором они определены, их нельзяперегрузить.В C# члены типов, помимо методов, полей,конструкторов и вложенных типов, могут бытьконстантами, свойствами, индексированнымисвойствами, событиями или операторами.Кроме этого, в типе можно определитьдеструктор и статический конструктор.Нестатические свойства, индексированныесвойства и события можно перегружать внаследниках.
Остальные из перечисленныхэлементов относятся только к тому классу, вкотором описаны.Для многих из дополнительных разновидностей членов типов, имеющихся в C#, естьаналогичные идиомы в компонентной модели JavaBeans [8,9], предназначенной для построенияэлементов пользовательского интерфейса и широко используемой в рамках Java технологий длясоздания компонентов, структуру которых можно анализировать динамически на основепредлагаемых JavaBeans соглашений об именовании методов. Далее вместе с примерами кода наC# в правом столбце в левом приводится аналогичный код, написанный в соответствии JavaBeans.Константы в Java принято оформлять в видеполей с модификаторами final static.Модификатор final для поля означает, чтоКонстанты являются единождывычисляемыми и неизменными далеезначениями, хранящимися в классе или177присвоить ему значение можно только один рази сделать это нужно либо в статическоминициализаторе класса (см.
ниже), если полестатическое, либо в каждом из конструкторов,если поле нестатическое.структуре.Пример объявления константы приведен ниже.public class A2{public static final double PHI =1.61803398874989;}public class A2{public const double Phi =1.61803398874989;}Компонентная модель JavaBeans определяетсвойство (property) класса A, имеющее имяname и тип T, как набор из одного или двухметодов, декларированных в классе A — TgetName() и void setName(T), называемыхметодами доступа (accessor methods) ксвойству.Свойство может быть доступным только длячтения, если имеется лишь метод get, и толькодля записи, если имеется лишь метод set.Если свойство имеет логический тип, дляметода чтения этого свойства используется имяisName().Эти соглашения широко используются вразработке Java программ, и такие свойстваописываются не только у классов,предназначенных стать компонентамиJavaBeans.Они и стали основанием для введенияспециальной конструкции для описания свойствв C#.Свойства (properties) представляют собой«виртуальные» поля.
Каждое свойство имеетодин или оба метода доступа get и set,которые определяют действия, выполняемыепри чтении и модификации этого свойства. Обаметода доступа описываются внутридекларации свойства. Метод set используетспециальный идентификатор value для ссылкина устанавливаемое значение свойства.Обращение к свойству — чтение (возможно,только если у него есть метод get) илиизменение значения свойства (возможно, толькоесли у него есть метод set) — происходит также, как к полю.При перегрузке свойства в наследникахперегружаются методы доступа к нему.Пример объявления свойств и их использованияприведен ниже.using System;public class MyArrayList{private int[] items = new int[10];private int size = 0;public class MyArrayList{private int[] items = new int[10];private int size = 0;public int getSize(){return size;}public int Size{get { return size; }}public int getCapacity(){return items.Length;}public int Capacity{get { return items.Length; }set{int[] newItems = new int[value];Array.Copy(items, newItems, size);items = newItems;}}public void setCapacity(int value){int[] newItems = new int[value];System.arraycopy(items, 0, newItems, 0, size);items = newItems;}public static void main(String[] args){public static void Main(){178MyArrayList l = new MyArrayList();System.out.println(l.getSize());System.out.println(l.getCapacity());l.setCapacity(50);System.out.println(l.getSize());System.out.println(l.getCapacity());}}JavaBeans определяет индексированноесвойство (indexed property) класса A, имеющееимя name и тип T, как один или пару методов TgetName(int) и void setName(int, T).Свойства могут быть индексированы толькоодним целым числом.