sdt-book-2006 (1133574), страница 49
Текст из файла (страница 49)
Конструкции174super в Java и base в C# можно использовать и для обращения к операциям, декларированным впредке данного класса. Для обращения к полям и операциям самого объекта в обоих языкахможно использовать префикс this, являющийся ссылкой на объект, в котором вызывается даннаяоперация.Основная выгода от использования наследования — возможность перегружать (override)реализации операций в типах-наследниках. Это значит, что при вызове операции с даннойсигнатурой в объекте наследника может быть выполнена не та реализация этой операции, котораяопределена в предке, а совсем другая, определенная в точном типе объекта.
Такие операцииназывают виртуальными (virtual). Чтобы определить новую реализацию некоторой виртуальнойоперации предка в потомке, нужно определить в потомке операцию с той же сигнатурой. При этомнеобходимо следовать общему принципу, обеспечивающему корректность системы типов в целом— принципу подстановки (Liskov substitution principle) [4,5]. Поскольку тип-наследник являетсяболее узким, чем тип-предок, его объект может использоваться всюду, где может использоватьсяобъект типа-предка. Принцип подстановки, обеспечивающий это свойство, требует соблюдениядвух правил.• Во всякой ситуации, в которой можно вызвать данную операцию в предке, ее вызов долженбыть возможен и в наследнике.
Говоря по-другому, предусловие операции при перегрузкене должно усиливаться.• Множество ситуаций, в которых система в целом может оказаться после вызова операции внаследнике, должно быть подмножеством набора ситуаций, в которых она может оказатьсяв результате вызова этой операции в предке. То есть, постусловие операции при перегрузкене должно ослабляться.Статические операции, относящиеся к классу в целом, а не к его объектам, не виртуальны. Онине могут быть перегружены, но могут быть перекрыты, если в потомке определяются статическиеоперации с такими же сигнатурами.В 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присвоить ему значение можно только один рази сделать это нужно либо в статическоминициализаторе класса (см.