В.В. Кулямин - Технологии программирования. Компонентный подход (1134162), страница 53
Текст из файла (страница 53)
Иначе надоиспользовать модификатор explicit и всегдаявно приводить один тип к другому.Некоторые операторы можно определять толькопарами — таковы true и false, == и !=, < и >,182<= и >=.Операторы true и false служат для неявногопреобразования объектов данного типа ксоответствующим логическим значениям.Если в типе T определяются операторы & и |,возвращающие значение этого же типа, а такжеоператоры true и false, то к объектам типаможно применять условные логическиеоператоры && и ||. Их поведение в этом случаеможет быть описано соотношениями (x && y)= (T.false(x)? x : (x & y)) и (x || y) =(T.true(x)? x : (x | y)).Аналогичным образом автоматическипереопределяются составные операторыприсваивания, если переопределены операторы+, -, *, /, %, &, |, ^, << или >>.Ниже приведен пример переопределения ииспользования операторов.
Обратите внимание,что оператор приведения типа MyInt в intдействует неявно, а для обратного переходатребуется явное приведение.Другая тонкость — необходимость приведенияобъектов типа MyInt к object в методеAreEqual — если этого не сделать, то приобращении к оператору == возникнетбесконечный цикл, поскольку сравнение i1 ==null тоже будет интерпретироваться как вызовэтого оператора.using System;public class MyInt{int n = 0;public MyInt(int n) { this.n = n; }public override bool Equals(object obj){MyInt o = obj as MyInt;if (o == null) return false;return o.n == n;}public override int GetHashCode(){ return n; }public override string ToString(){ return n.ToString(); }public static bool AreEqual(MyInt i1, MyInt i2){if ((object)i1 == null)return ((object)i2 == null);else return i1.Equals(i2);}183public static bool operator ==(MyInt i1, MyInt i2){ return AreEqual(i1, i2); }public static bool operator !=(MyInt i1, MyInt i2){ return !AreEqual(i1, i2); }public static bool operator true(MyInt i){ return i.n > 0; }public static bool operator false(MyInt i){ return i.n <= 0; }public static MyInt operator ++(MyInt i){ return new MyInt(i.n + 1); }public static MyInt operator -(MyInt i){ return new MyInt(i.n - 1); }public static MyInt operator &(MyInt i1, MyInt i2){ return new MyInt(i1.n & i2.n); }public static MyInt operator |(MyInt i1, MyInt i2){ return new MyInt(i1.n | i2.n); }public static implicit operator int(MyInt i){ return i.n; }public static explicit operatorMyInt (int i){ return new MyInt(i); }}Аналогом деструктора в Java является методprotected void finalize(), который можноpublic static void Main(){MyInt n = (MyInt)5;MyInt k = (MyInt)(n - 3 * n);Console.WriteLine("k = " + k +" , n = " + n);Console.WriteLine("n == n : " +(n == n));Console.WriteLine("n == k : " +(n == k));Console.WriteLine("(++k) && (n++) : " +((++k) && (n++)));Console.WriteLine("k = " + k +" , n = " + n);Console.WriteLine("(++n) && (k++) : " +((++n) && (k++)));Console.WriteLine("k = " + k +" , n = " + n);}Деструктор предназначен для освобождениякаких-либо ресурсов, связанных с объектом ине освобождаемых автоматически средой .NET,184перегрузить для данного класса.Так же, как и деструктор в C#, этот методвызывается на некотором шаге уничтоженияобъекта после того, как тот был помеченсборщиком мусора как неиспользуемый.либо для оптимизации использования ресурсовза счет их явного освобождения.Деструктор вызывается автоматически приуничтожении объекта в ходе работы механизмауправления памятью .NET.
В этот моментобъект уже должен быть помечен сборщикоммусора как неиспользуемый.Деструктор оформляется как особый метод, безвозвращаемого значения и с именем,получающимся добавлением префикса ‘~’ кимени классаusing System;public class MyFileReader{java.io.FileReader input;}public class MyFileStream{System.IO.FileStream input;public MyFileReader(String path)throws FileNotFoundException{input = new java.io.FileReader(new java.io.File(path));}public MyFileStream(string path){input = System.IO.File.Open(path, System.IO.FileMode.Open);}protected void finalize(){System.out.println("Destructor");try { input.close(); }catch (IOException e){e.printStackTrace();}}~MyFileStream(){Console.WriteLine("Destructor");input.Close();}Инициализаторы представляют собой блокикода, заключенные в фигурные скобки ирасположенные непосредственно внутридекларации класса.Эти блоки выполняются вместе синициализаторами отдельных полей —выражениями, которые написаны после знака =в объявлениях полей — при построении объектаданного класса, в порядке их расположения вдекларации.Статические инициализаторы — такие жеблоки, помеченные модификатором static —выполняются вместе с инициализаторамистатических полей по тем же правилам вмомент первой загрузки класса в Java-машину.}Статический конструктор классапредставляет собой блок кода, выполняемыйпри первой загрузке класса в среду .NET, т.е.
вмомент первого использования этого класса впрограмме. Это аналог статическогоинициализатора в Java.Оформляется он как конструктор смодификатором static.using System;public class A{staticpublic class A{static A()185{}{System.out.println("Loading A");static int x = 1;static{System.out.println("x = " + x);x++;}}}A");x);x);y);static int x = 1;static int y = 2;static int y = 2;static{y = x + 3;System.out.println("x = " + x);System.out.println("y = " + y);}Console.WriteLine("LoadingConsole.WriteLine("x = " +x++;y = x + 3;Console.WriteLine("x = " +Console.WriteLine("y = " +public static void Main() {}}public static void main(String[] args){}Приведенный выше код выдает результатПриведенный выше код выдает результатLoading Ax = 1x = 2y = 5Loading Ax = 1x = 2y = 5В Java нестатические вложенные типытрактуются очень специфическим образом —каждый объект такого типа считаетсяпривязанным к определенному объектуобъемлющего типа.
У нестатическоговложенного типа есть как бы необъявленноеполе, хранящее ссылку на объект объемлющеготипа.Такая конструкция используется, например, дляопределения классов итераторов для коллекций— объект-итератор всегда связан с объектомколлекцией, которую он итерирует. В то жевремя, пользователю не нужно знать, какогоименно типа данный итератор, — достаточно,что он реализует общий интерфейс всехитераторов, позволяющий проверить, есть лиеще объекты, и получить следующий объект.Получить этот объект внутри декларациивложенного типа можно с помощьюконструкции ClassName.this, где ClassName —имя объемлющего типа.При создании объекта такого вложенногокласса необходимо указать объектобъемлющего класса, к которому тот будетпривязан.В C# модификатор static у класса, все равно,вложенного в другой или нет, обозначает, чтоэтот класс является контейнером набораконстант и статических операций.
Все егоэлементы должны быть декларированы какstatic.public class ContainingClass{static int counter = 1;static int ecounter = 1;186int id = counter++;class EmbeddedClass{int eid = ecounter++;}public String toString(){return "" +ContainingClass.this.id +'.' + eid;}public String toString(){return "" + id;}public static void main(String[] args){ContainingClassc = new ContainingClass(), c1 = new ContainingClass();System.out.println(c);System.out.println(c1);}}EmbeddedClasse = c.new EmbeddedClass(), e1 = c.new EmbeddedClass(), e2 = c1.new EmbeddedClass();System.out.println(e);System.out.println(e1);System.out.println(e2);В C# класс может определить различныереализации для операций (методов, свойств,индексеров, событий) с одинаковой сигнатурой,если они декларированы в различныхреализуемых классом интерфейсах.Для этого при определении таких операцийнужно указывать имя интерфейса в качестверасширения их имени.using System;public interface I1{void m();}public interface I2{void m();}public class A : I1, I2{public void m(){Console.WriteLine("A.m() called");}187void I1.m(){Console.WriteLine("I1.m() defined in A called");}void I2.m(){Console.WriteLine("I2.m() defined in A called");}public static void Main(){A f = new A();I1 i1 = f;I2 i2 = f;}}f.m();i1.m();i2.m();Результат работы приведенного выше примераследующий.A.m() calledI1.m() defined in A calledI2.m() defined in A calledПоследовательность выполнения инициализаторов полей и конструкторов классов-предков инаследников при построении объектов в Java и C# различается достаточно сильно.О правилах, определяющих эту последовательность, можно судить по результатам работыследующих примеров.using System;public class A{static{System.out.println("Static initializer of A");}{}public class A{static A(){Console.WriteLine("Static constructor of A");}System.out.println("Initializer of A");static int sinit(){System.out.println("Static field initializer of A");return 0;}static int sinit(){Console.WriteLine("Static field initializer of A");return 0;}static int init(){System.out.println("Field initializer of A");return 0;}static int init(){Console.WriteLine("Field initializer of A");return 0;}static int sf = sinit();static int sf = sinit();188}int f = init();int f = init();public A(){System.out.println("Constructor of A");}public A(){Console.WriteLine("Constructor of A");}public class B extends A{static int sf = sinit();int f = init();}public class B : A{static int sf = sinit();int f = init();static B(){Console.WriteLine("Static constructor of B");}static{System.out.println("Static initializer of B");}{}}System.out.println("Initializer of B");static int sinit(){System.out.println("Static field initializer of B");return 0;}static int sinit(){Console.WriteLine("Static field initializer of B");return 0;}static int init(){System.out.println("Field initializer of B");return 0;}static int init(){Console.WriteLine("Field initializer of B");return 0;}public B(){System.out.println("Constructor of B");}public B(){Console.WriteLine("Constructor of B");}public class C{public static void main(String[] args){B b = new B();}}}public class C{public static void Main(){B b = new B();}}Результат работы приведенного примера такой.Результат работы приведенного примера такой.Static initializer of AStatic field initializer of AStatic field initializer of BStatic initializer of BInitializer of AField initializer of AConstructor of AField initializer of BInitializer of BConstructor of BStatic field initializer of BStatic constructor of BField initializer of BStatic field initializer of AStatic constructor of AField initializer of AConstructor of AConstructor of B189В Java элемент типа, помимо доступности,указываемой с помощью модификаторовprivate, protected и public, может иметьпакетную доступность.