Книга е още в много ранна фаза на написване



страница69/73
Дата25.07.2016
Размер13.53 Mb.
#6732
1   ...   65   66   67   68   69   70   71   72   73

Резюме


RTTI позволява да се получи информация за типа от анонимен манипулатор на ба­зов клас. Така то е готово за неправилна употреба от новака понеже може да има смисъл преди полиморфните методи. За много хора с процедурна ориен­та­ция, it’s difficult not to organize their programs into sets oе трудно да реор­га­ни­зи­рат програмите си в множества от switch оператори. Те биха направили това чрез RTTI и биха загубили по този начин важната черта полиморфизъм на кода и поддръжката. Намерението в Java е да се използват полиморфни извиквания на методи в кода, а да се използва RTTI само когато е необходимо.

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

Слагането на черта в базовия клас би могло да значи че, за употреба в някакъв спе­циален слас, всички класове извлечени от въпросния клас трябва да имат ня­ка­ква част от метода. Това прави интерфейса по-малко ясен и дразни с под­тис­ка­нето на абстрактните методи когато наследявате от този базов клас. На­при­мер да вземем класова йерархия представяща музикални инструменти. Да ка­жем че искате да настроите всички инструменти в оркестъра. Едната въз­мож­ност е да използвате ClearSpitValve( ) метод в базовия клас Instrument, но това е смущаващо понеже предполага че Percussion и Electronic инструментите също се настройват с червячета. RTTI дава много по-смислено решение в този слу­чай понеже можете да сложите метода в специфичен клас (Wind в този случай), където е подходящо. Обаче по-подходящо решение е да сложите prepareInstrument( ) метод в базовия клас, но може да не видите това когато се сблъ­скате с проблема и погрешно да мислите че трябва да се използва RTTI.

Накрая, RTTI понякога ще решава проблеми с ефективността. Ако вашят код използва масово полиморфизъм, но излезе че един от вашите обекти реагира на това с твърде ниска ефикасност, може да намерите типа с RTTI и да напишете код зависещ от случая за повишаване на ефективността.


Упражнения


  1. Напишете код който взема обект и рекурсивно печата всички класове в йерархията му.

  2. В ToyTest.java, изкоментирайте конструктора по подразбиране на Toy и обаснете какво се случва.

  3. Създайте нов тип колекция която използва ArrayList. Хванете типа на пър­вия вкаран обект, а после позволете на потребителя да вкарва обекти от са­мо него тип от този момент нататък.

  4. Напишете програма да определи дали масив от char е примитивен тип или същински обект.

  5. Реализирайте clearSpitValve( ) както е описано в тази глава.

  6. Реализирайте rotate(Shape) метода описан в тази глава, така че да проверява дали върти Circle (и, ако да, не изпълнява операцията).

12: Подаване и връщане на обекти


Вече трябва да се чувствате достатъчно удобно с мисълта че когато “предавате” обект фактически предавате манипулатор.

В много програмни езици, ако не във всички, може да се използва “обичаен” начин да се предават обекти и повечето време той работи добре. Но винаги, из­глеж­да, идва момент когато се налага да правите нещо необичайно и нещата ста­ват малко по-сложни (в случая на C++, доста сложни). Java не е изключение, важ­но е да знаете какво точно става при предаването на обекти. Тази глава ще хвър­ли светлина върху този въпрос.

Друг начин да се постави въпроса, ако извате от съответно снабден език, е “Има ли в Java указатели?” Твърди се че указателите са сложни и пр. И затова ло­ши, а понеже Java е изцяло доброта и светлина and и ще премахне вашите зем­ни програмистки тегла, той вероятно не може да има такива неща. Обаче по-точ­но е да се каже че в Java има указатели; разбира се всеки идентификатор на обект в Java (освен за примитивите) е един от тези указатели, но използването им е ограничавано и ковтролирано не само от компилатора, но и от опе­ра­цион­на­та среда по време наизпълнение. Или с други думи казано, Java има ука­за­те­ли, но не аритметика с указателите. Те са които аз нарекох “манипулатори,” вие мо­же да ги мислите като “безопасни указатели,” не много различно от безо­пас­ни­те ножици в основното училище - те не са остри и не може да се порежете без го­лямо усилие, но понякога могат да бъдат бавни и досадни.

Подаване на манипулатори


Когато подавате манипулатор на метад, още сочите към същия обект. Прост експе­римент демонстрира това: (Виж стр. 89 ако имате проблеми с пускането на тази програма.)

//: c12:PassHandles.java

// Passing handles around

package c12;


public class PassHandles {

static void f(PassHandles h) {

System.out.println("h inside f(): " + h);

}

public static void main(String[] args) {



PassHandles p = new PassHandles();

System.out.println("p inside main(): " + p);

f(p);

}

} ///:~



Методът toString( ) е автоматично извикан в операторите за извеждане, а PassHandles наследява директно от Object без предефиниране на toString( ). Така, версията на toString( ) от Object се използва, което извежда класа на обек­та следван от адреса на който се намира обекта (не манипулатора, а фак­ти­че­ския адрес в паметта). Изходът изглежда като този:

p inside main(): PassHandles@1653748

h inside f(): PassHandles@1653748

Може да видите че както p така и h се отнасят за един и същ обект. Това е мно­го по-ефектимно от дублицирането на PassHandles обекта така че да можее да по­дадете аргумент на метод. Но това поражда важен въпрос.


Псевдоними


Псевдонимите означават, че повече от един указател сочи към един обект, как­то в горния пример. Проблемът с псевдонимите се случва когато някой пише в не­го обект. Ако собствениците на другите манипулатори към обекта не очакват той да се променя, те ще бъдат изненадани. Това може да бъде демонстрирано с прост пример:

//: c12:Alias1.java

// Aliasing two handles to one object
public class Alias1 {

int i;


Alias1(int ii) { i = ii; }

public static void main(String[] args) {

Alias1 x = new Alias1(7);

Alias1 y = x; // Assign the handle

System.out.println("x: " + x.i);

System.out.println("y: " + y.i);

System.out.println("Incrementing x");

x.i++;


System.out.println("x: " + x.i);

System.out.println("y: " + y.i);

}

} ///:~


В реда:

Alias1 y = x; // Assign the handle

нов Alias1 манипулатор се създава, но вместо да се насочи към пресния обект създаден с new, той се приравнява на съществуващия манипулатор. Така че съдържанието на манипулатора x, който е адрес на обекта x към който сочи, е приравнен на y, а с това и x и y сочат един и същ обект. Така че когато i-то на x се увеличава в оператора:

x.i++;


i-то на y също ще бъде засегжнато. Това може да бъде видяно в изхода:

x: 7


y: 7

Incrementing x

x: 8

y: 8


Едно добро решение на случая е просто той да не се състои: не правете алиа­синг съзнателно в един обхват. Вашият код ще бъде по-лесен за четене и тест­ва­не. Обаче когато давате манипулатор като аргумент – което е начина по който се предполага да се работи в Java – понеже манипулаторът който автоматично се създава локално може да модифицира “външния обект” (обектът който е съз­да­ден извън обхвата на метода). Ето пример:

//: c12:Alias2.java

// Method calls implicitly alias their

// arguments.


public class Alias2 {

int i;


Alias2(int ii) { i = ii; }

static void f(Alias2 handle) {

handle.i++;

}

public static void main(String[] args) {



Alias2 x = new Alias2(7);

System.out.println("x: " + x.i);

System.out.println("Calling f(x)");

f(x);


System.out.println("x: " + x.i);

}

} ///:~



Изходът е:

x: 7


Calling f(x)

x: 8


Методът променя аргумента си, външния обект. Когато се случи нещо от този род, вие трябва да решите смислено ли е, дали потребителят го обаква и дали ще се създадат проблеми.

Изобщо, методи се викат за да върнат стойност и/или промяна на състоянието на обекта за който методът е извикан. (Методът е как “пращате съобщение” към него обект.) Много по-малко разпространено е да се вика метод заради про­мяна на аргументите му; това се нарича “викане на мутод заради странични ефек­ти.” Така, ако го правите, потребителят трябва да е добре инструктирани пре­дупреден за възможни изненади. Поради смущението и капаните, много по-до­бре е да избягвате страничните ефекти.



Ако искате да промените аргумент по време на работа на метод и не въз­на­ме­ря­ва­те да променяте външния аргумент, ще предпазите последния чрез създаване на копие в метода. Това е тема на по-голямата част от тази глава.



Сподели с приятели:
1   ...   65   66   67   68   69   70   71   72   73




©obuch.info 2024
отнасят до администрацията

    Начална страница