Руководство по actionscript. часть 2, стр. 082
Throw new VirtualPetNameException( ): } else if (newName == «») {
// Вот пользовательское сообщение об ошибке «слишком короткое».
throw new VirtualPetNameException(«Pet name too short.»); } else if (newName. length > Virtual Pet. maxNameLength) {
// Вот пользовательское сообщение об ошибке «слишком длинное».
throw new Virtual PetNameException(«Pet name too long.»);
}
petName = newName;
}
Теперь, когда метод setName ( ) задает пользовательские сообщения об ошибках, упрощается отладка проблем, связанных с именем объекта класса VirtualPet, поскольку у нас появляется возможность получить больше информации о возникшей ошибке. Использование метода setName ( ) не изменилось, но теперь, если что-то пойдет не так, мы будем лучше проинформированы, как показано в следующем коде:
try {
// Этот вызов метода setName( ) приведет к возникновению исключения
// VirtualPetNameException.
somePet. setName(«»); } catch (e:VirtualPetNameException) {
// Здесь обрабатываются ошибки, связанные с именем объекта класса
// VirtualPet. В данном случае полезным отладочным сообщением является:
// An error occurred: Pet name too short
// (Возникла ошибка: Имя животного слишком короткое).
Руководство по actionscript. часть 2, стр. 083
TraceC’An error occurred: » + е. message); } catch (e:Error) {
// Здесь обрабатываются все остальные ошибки.
traceC’An error occurred: » + е. message);
}
Вариант 4. Пользовательские подклассы класса VirtualPetNameException.
В варианте 3 мы добавляли конфигурируемые отладочные сообщения в класс Virtual PetNameExcept ion. Он помог выявить проблему в нашем коде на этапе разработки, однако этот вариант не позволяет программе выполнить независимые действия по восстановлению работоспособности после возникновения отдельных
ошибок класса VirtualPet. Чтобы программа могла выполнить отдельные ветки кода в зависимости от типа сгенерированной ошибки класса VirtualPet, нам потребуются пользовательские подклассы класса VirtualPetNameException, описанные в варианте 4.
I Если вы хотите, чтобы программа могла находить различия между исключительными м$ j * ситуациями, определяйте отдельный подкласс класса Error для каждой ошибки. Не по-
_ ц# лагайтесь исключительно на значение переменной message, чтобы реализовать логику
ветвлений. Если ваш пользовательский подкласс класса Error определяет конструктор, принимающий сообщение об ошибке в качестве параметра, используйте это сообщение только для отладки, но не для построения логики ветвлений.
Чтобы иметь возможность устанавливать различие между тремя исключительными ситуациями класса VirtualPet, создадим три подкласса класса Error: VirtualPetNameException, Virtual Pet Insufficient DataExcept ion и Vir tualPetExcessDataException. Первый класс непосредственно расширяет класс Error. Оба следующих класса расширяют класс VirtualPetNameException, поскольку мы хотим отличать эти специфические типы ошибок от базового исключения, обозначающего недопустимые данные.
Рассмотрим исходный код наших трех подклассов класса Error, представляющих ошибки класса VirtualPet:
// Код в файле VirtualPetNameException. as:
package zoo {
public class VirtualPetNameException extends Error { public function VirtualPetNameException (
message:String = «Invalid pet name specified.») {
super(message);
}
}
}
// Код в файле Virtual PetInsufficientDataException. as:
package zoo { public class VirtualPetlnsufficientDataException
extends VirtualPetNameException { public function VirtualPetlnsufficientDataException ( ) { super(«Pet name too short.»);
}
}
}
// Код в файле VirtualPetExcessDataException. as:
package zoo { public class VirtualPetExcessDataException
extends VirtualPetNameException { public function VirtualPetExcessDataException ( ) { super(«Pet name too long.»);
Каждый класс определяет значение своей переменной message и не позволяет изменять его в процессе использования. При обработке любого из описанных исключений класса VirtualPet наша программа будет руководствоваться типом данных исключения (а не значением переменной message) для нахождения различия между тремя типами исключений.
Руководство по actionscript. часть 2, стр. 084
Теперь, когда у нас появилось три типа исключений, добавим их генерацию в наш метод setName ( ):
public function setName (newName:String):void { if (newName. indexOfC «) == 0) {
throw new VirtualPetNameException( ); } else if (newName == «») {
throw new VirtualPetInsufficientDataException( ); } else if (newName. length > Virtual Pet. maxNameLength) {
throw new VirtualPetExcessDataException( );
}
petName = newName;
}
Обратите внимание, что в конструкторы различных исключений класса Virtual Pet не передаются никакие описания ошибок. Еще раз повторим, что описание каждого исключения задается в соответствующем пользовательском подклассе класса Error через его переменную message.
Теперь, когда каждое исключение класса VirtualPet представлено собственным классом, программистам, работающим с экземплярами класса VirtualPet, известны все ошибки, которые могут быть сгенерированы методом setName ( ). Типы исключений доступны за пределами класса VirtualPet и, соответственно, открыты для программистов, работающих над приложением. Разработчику достаточно взглянуть на иерархию классов приложения, чтобы определить исключения, относящиеся к классу VirtualPet. Более того, если он случайно укажет неправильное имя для исключения, компилятор сгенерирует ошибку типа.
Рассмотрим, как добавить в код логику ветвления, основанную на типах исключений, которые могут быть сгенерированы методом setName ( ). Особое внимание обратите на типы данных параметров и размещение каждого блока catch.
try {
b. setNameCнекоеИмяЖивотного»); } catch (e:VirtualPetExcessDataException) {
// Обработка ситуации «слишком длинное».
Руководство по actionscript. часть 2, стр. 085
TraceC’An error occurred: » + e. message);
traceCPlease specify a shorter name.»); } catch (e:VirtualPetlnsufficientDataException) {
// Обработка ситуации «слишком короткое».
traceC’An error occurred: » + e. message);
traceCPlease specify a longer name.»); } catch (e:VirtualPetNameException) {
// Обработка общих ошибок, связанных с именем.
traceC’An error occurred: » + е. message);
traceCPlease specify a valid name.»);
В приведенном коде, если метод setName ( ) сгенерирует исключение Virtu alPetExcessDataException, будет выполнен первый блок catch. Если метод сгенерирует исключение VirtualPetlnsufficientDataException, будет выполнен второй блок catch. И наконец, если метод сгенерирует исключение VirtualPetNameException, будет выполнен третий блок catch. Обратите внимание, что в блоках catch сначала перечислены специфические типы данных ошибок, а затем — общие. При возникновении исключения выполняется тот блок catch, у которого первым совпадет тип данных параметра с типом данных исключения.
Таким образом, если мы изменим тип данных параметра первого блока catch на тип VirtualPetNameException, первый блок catch будет выполняться для всех трех типов исключений!
Вспомните, что класс VirtualPetNameException является суперклассом для обоих классов VirtualPetlnsufflcientDataException и VirtualPetExcessDataException, поэтому считается, что они соответствуют типу данных VirtualPetNameException.
Фактически мы могли бы предотвратить выполнение всех блоков catch, разместив первым новый блок catch, типом данных параметра которого является Error:
try {
b. setName(«некоеИмяЖивотного»); } catch (e:Error) {
// Обрабатываем все ошибки. Никакие другие блоки catch
// выполняться не будут.
Руководство по actionscript. часть 2, стр. 086
TraceC’An error occurred:» + е. message);
traceC’The first catch block handled the error.»); } catch (e:VirtualPetExcessDataException) {
// Обработка ситуации «слишком длинное».
traceC’An error occurred: » + e. message);
traceCPlease specify a shorter name.»); } catch (e:\ZirtualPetInsufficientDataException) {
// Обработка ситуации «слишком короткое».
traceC’An error occurred: » + e. message);
traceCPlease specify a longer name.»); } catch (e:VirtualPetNameException) {
// Обработка общих ошибок, связанных с именем.
traceC’An error occurred: » + е. message);
traceCPlease specify a valid name.»);
}
Очевидно, что попытка добавить первый блок catch в предыдущем коде обречена на провал, но этот пример иллюстрирует иерархическую природу обработки ошибок. Поместив базовый блок catch в самое начало списка обработчиков, мы можем обработать все ошибки в одном блоке. И наоборот, если поместить базовый блок catch в конец списка, мы можем создать «страховочную сетку», которая будет обрабатывать любые ошибки, не «пойманные» предыдущими блоками catch. Например, в следующем коде последний блок catch будет выполнен только в том
случае, если блок try сгенерирует исключение, которое не принадлежит типам данных VirtualPetExcessDataException, Virtual Pet In sufficient Data Exception или VirtualPetNameException:
try {
b. setName(«некоеИмяЖивотного»); } catch (e:VirtualPetExcessDataException) {
// Обработка переполнения.
Руководство по actionscript. часть 2, стр. 087
TraceC’An error occurred: » + e. message);
traceCPlease specify a smaller value.»): } catch (e:VirtualPetlnsufficientDataException) {
// Обработка нулевой длины.
traceC’An error occurred: » + e. message);
traceCPlease specify a larger value.»); } catch (e:VirtualPetNameException) {
// Обработка общих ошибок, связанных с размерностью.
traceC’An error occurred: » + е. message);
traceCPlease specify a valid dimension.»); } catch (e:Error) {
// Обработка любых ошибок, которые не относятся
// к ошибкам VirtualPetNameException.
}
Не забывайте, что выбор степени детализации ошибок зависит от ситуации. Реализуя вариант 4, мы создали пользовательские подклассы класса Error для каждого исключения, генерируемого классом VirtualPet. Этот подход дает нашей программе максимальную возможность независимо обрабатывать различные типы ошибок. Однако во многих ситуациях подобная гибкость является излишней. Будет лучше, если степень детализации ошибок будет определяться требованиями логики вашей программы.
Передача исключений вверх по иерархии объектов
В ActionScript исключение может быть сгенерировано в любом месте программы, даже в сценарии кадра на временной шкале! В этом случае возникает вопрос: каким образом среда выполнения Flash находит соответствующий блок catch для обработки этого исключения? И что произойдет при отсутствии блоков catch? Эти загадки решаются с. помощью магии передачи исключений вверх по иерархии объектов. Проследуем по пути передачи исключений вместе со средой Flash с того момента, как она выполнит в программе оператор throw. В ходе последующей инсценировки «размышления» среды выполнения Flash оформлены в виде комментариев.
Руководство по actionscript. часть 2, стр. 088
После исполнения оператора throw нормальная работа программы немедленно прекращается и среда Flash пытается найти блок try, который содержит этот оператор. Например, рассмотрим оператор throw:
// Среда выполнения Flash: Хм. Оператор throw.
// Существует ли блок try, который содержит этот оператор?
throw new Error(«Something went wrong»);
Если оператор throw включен в блок try, среда выполнения Flash пытается найти блок catch, тип данных параметра которого совпадает с типом данных значения сгенерированного исключения (в данном случае с типом Error):
// Среда выполнения Flash: Отлично, я нашла блок try. // Существует ли соответствующий блок catch? try {
throw new Error(«Something went wrong»);
}
Если соответствующий блок catch найден, среда выполнения Flash передает управление программой этому блоку:
try {
throw new Error(«Something went wrong»); // Среда выполнения Flash: Найден блок catch, типом данных параметра // которого является Error! Поиск завершен. Сейчас я выполню этот // блок catch… } catch (e:Error) {
// Обработка ошибок…
}
Однако если соответствующий блок catch не может быть найден или если оператор throw изначально не был включен в блок try, среда Flash проверяет, размещен ли этот оператор внутри метода или функции. Если да, то среда выполнения ищет блок try вокруг кода, вызвавшего этот метод или функцию. Следующий код демонстрирует, как среда Flash реагирует на оператор throw, который размещен внутри метода и не включен в блок try:
public function doSomething ( ):void { // Среда выполнения Flash: Хм. Блок try отсутствует. // Проверю, кто вызвал этот метод, throw new Error(«Something went wrong»);
}
Если код, вызвавший этот метод или функцию, включен в блок try, среда Flash попытается найти соответствующий блок catch и, если такой блок будет найден, выполнит его. Следующий код демонстрирует исключение, генерируемое в методе и обрабатываемое там, где вызывается этот метод (то есть уровнем выше по стеку вызовов):
public class ProblemClass { public function doSomething ( ):void { // Среда выполнения Flash: Хм. Блок try отсутствует. // Проверю-ка я, кто вызвал этот метод, throw new Error(«Something went wrong»);
}
}
// Среда выполнения Flash: Ага, вот кто вызвал метод doSomething( ). // И вот блок try, включающий этот код, вместе с блоком catch, типом // данных параметра которого является Error! Моя работа сделана. // Блок catch, пожалуйста, выполняйтесь… try {
var problemObject:ProblemClass = new ProblemClass( ); problemObject. doSomething( ); } catch (e:Error) { // Обработка ошибок…
Руководство по actionscript. часть 2, стр. 089
Trace(«Exception caught in ErrorDemo, thrown by doSomething( ).»);
}
}
Стек вызовов — это список функций или методов программы, исполнением которых среда А щ Flash занимается в любой момент времени. Функции и методы размещаются в списке 3-У в порядке, обратном порядку их вызова, по направлению сверху вниз. Если функция
находится непосредственно под другой функцией в стеке вызовов, значит, нижняя
функция была вызвана верхней функцией. Самая нижняя функция в стеке вызовов — это
функция, выполняемая в настоящий момент.
В приложении Flex Builder и среде разработки Flash вы можете использовать отладчик для просмотра стека вызовов текущей программы, как описано в документации корпорации Adobe.
В предыдущем коде исключение, сгенерированное методом, было поймано блоком try/catch, в который включена инструкция вызова метода. Тем не менее, если вокруг кода, вызывающего функцию или метод, не найден блок try, среда выполнения Flash просматривает весь стек вызовов в поисках блока try с соответствующим блоком catch. Следующий код демонстрирует метод, генерирующий ошибку, которая обрабатывается двумя уровнями выше в стеке вызовов:
public class ProblemClass { public function doSomething ( ):void { // Среда выполнения Flash: Хм. Блок try отсутствует.^ // Проверю-ка я, кто вызвал этот метод, throw new Error(«Something went wrong»):
}
public class NormalClass { public function NormalClass ( ) { // Среда выполнения Flash: Ага, вот кто вызвал метод doSomething( ). // Но здесь все равно нет блока try. Проверю,
// кто вызвал этот метод,
var problemObject:ProblemClass = new ProblemClass( ); problemObject. doSomething( );
}
}
// Среда выполнения Flash: Ага! Нашла блок try, который имеет блок // catch, типом данных параметра которого
// является Error! Моя работа сделана.
Руководство по actionscript. часть 2, стр. 090
// Блок catch, пожалуйста, выполняйтесь…
try {
var normal Object:Normal CI ass = new NormalClass( ); } catch (e:Error) { // Обработка ошибок…
trace(«Exception caught in ErrorDemo, thrown by doSomething( ).»);
}
}
}
Обратите внимание, что среда выполнения Flash находит блок try/catch, даже несмотря на то, что этот блок не включает ни код, генерирующий ошибку, ни код, который вызывает метод, генерирующий ошибку, а только код, вызывающий метод, который, в свою очередь, вызывает метод, генерирующий ошибку!
Следующий код демонстрирует предыдущий пример с передачей исключения вверх по иерархии объектов в контексте нашей программы по созданию виртуального зоопарка. Для краткости в следующем листинге показан только код, присваивающий объекту имя животного. Комментарии в коде описывают, как происходит передача исключения.
package { import flash. display. Sprite; import zoo.*;
public class VirtualZoo extends Sprite { private var pet:Virtual Pet;
public function VirtualZoo ( ) { try {
// Этот код пытается присвоить животному слишком длинное имя. // В результате метод setName( ) генерирует ошибку. // Однако возникшее исключение не обрабатывается в конструкторе // класса VirtualPet (откуда вызывается метод setName( )). Вместо // этого исключение обрабатывается там, где вызывается конструктор // класса VirtualPet (то есть двумя уровнями выше в стеке вызовов) pet = new Virtual Pet(«Bartholomew McGillicuddy»); } catch (e:Error) { traceC’An error occurred: » + e. message);
// Если в процессе создания объекта VirtualPet возникает исключение, // объект не будет создан. Таким образом, здесь мы создадим новый // объект VirtualPet с предопределенным допустимым именем.
Руководство по actionscript. часть 2, стр. 091
Pet = new Virtual Pet(«Unnamed Pet»);
}
}
}
}
package zoo { public class VirtualPet {
public function VirtualPet (name:String):void { // Даже несмотря на то, что метод setName( ) вызывается здесь, // исключения, генерируемые методом setName( ), не обрабатываются // в данном конструкторе. Они обрабатываются выше в стеке вызовов // кодом, который создал данный объект VirtualPet. setName(name);
}
public function setName (newName:String):void { // Исключения, генерируемые в этом методе, не обрабатываются здесь. // Они обрабатываются на два уровня выше в стеке вызовов кодом, // который создал данный объект VirtualPet.
if (newName. indexOfC» «) == 0) {
throw new VirtualPetNameException( ); } else if (newName == «») {
throw new VirtualPetInsufficientDataException( ); } else if (newName. length > VirtualPet. maxNameLength) {
throw new VirtualPetExcessDataException( );
}
petName = newName:
}
}
}
Необработанные исключения. Мы рассмотрели ряд сценариев, в которых обрабатывались различные ошибки. Что же произойдет в том случае, когда среда выполнения Flash не найдет блок catch, способный обработать сгенерированное исключение? Если подходящий блок catch не найден во всем стеке вызовов, то Flash завершает выполнение кода, который на текущий момент остается в стеке вызовов. Кроме того, если программа выполняется в отладочной версии среды Flash, информация о возникшей ошибке появится в отдельном окне: в окне Output (Вывод) (среда разработки Flash) или в окне Console (Консоль) (приложение Flex Builder). После этого выполнение программы продолжится в обычном режиме.