русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

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


Дата добавления: 2015-06-12; просмотров: 874; Нарушение авторских прав


Итак, мы рассмотрели следующие формы RTTI:

· Классическое преобразование; аналог выражения «(Shape)», которое проверяет, «законно» ли приведение типов в данной ситуации, и в случае неверного преобразования возбуждает исключение ClassCastException.

· Объект Class, представляющий тип вашего объекта. К объекту Class можно обращаться для получения полезной информации во время выполнения программы.

В языке C++ классическая форма типа «(Shape)» вообще не задействует RTTI. Она просто сообщает компилятору, что необходимо обращаться с объектом как с новым типом. В языке Java, который при приведении проверяет соответствие типов, такое преобразование часто называют «безопасным нисходящим приведением типов». Слово «нисходящее» используется в силу традиций, сложившихся в практике составления диаграмм наследования. Если приведение окружности Circle к фигуре Shape является восходящим, то приведение фигурыShape к окружности Circle является, соответственно, нисходящим. Поскольку компилятор знает, что Circle является частным случаем Shape, он позволяет использовать «восходящее» присваивание без явного преобразования типа. Тем не менее, получив некий объектShape, компилятор не может быть уверен в том, что он получил: то ли действительно Shape, то ли один из производных типов (Circle, Square или Triangle). На стадии компиляции он видит только Shape и поэтому не позволит использовать «нисходящее» присваивание без явного преобразования типа.

Существует и третья форма RTTI в Java — ключевое слово instanceof, которое проверяет, является ли объект экземпляром заданного типа. Результат возвращается в логическом (boolean) формате, поэтому вы просто «задаете» вопрос в следующей форме:

іf(х instanceof Dog) ((Dog)x).bark().

Команда if сначала проверяет, принадлежит ли объект к классу Dog, и только после этого выполняет приведение объекта к типу Dog. Настоятельно рекомендуется использовать ключевое слово instanceof перед проведением нисходящего преобразования, особенно при недостатке информации о точном типе объекта; иначе возникает опасность исключения ClassCastException. Обычно проводится поиск одного определенного типа (например, поиск треугольников среди прочих фигур), но с помощью ключевого слова instanceof легко можно идентифицировать все типы объекта. Предположим, что у нас есть иерархия классов для описания домашних животных Pet (и их владельцев — эта особенность пригодится нам в более позднем примере). Каждое существо (Individual) в этой иерархии обладает идентификатором id и необязательным именем.



//: typeinfo/pets/Individual.java

package typeinfo.pets;

 

public class Individual implements Comparable<Individual> {

private static long counter = 0;

private final long id = counter++;

private String name;

public Individual(String name) { this.name = name; }

// 'name' is optional:

public Individual() {}

public String toString() {

return getClass().getSimpleName() +

(name == null ? "" : " " + name);

}

public long id() { return id; }

public boolean equals(Object o) {

return o instanceof Individual &&

id == ((Individual)o).id;

}

public int hashCode() {

int result = 17;

if(name != null)

result = 37 * result + name.hashCode();

result = 37 * result + (int)id;

return result;

}

public int compareTo(Individual arg) {

// Compare by class name first:

String first = getClass().getSimpleName();

String argFirst = arg.getClass().getSimpleName();

int firstCompare = first.compareTo(argFirst);

if(firstCompare != 0)

return firstCompare;

if(name != null && arg.name != null) {

int secondCompare = name.compareTo(arg.name);

if(secondCompare != 0)

return secondCompare;

}

return (arg.id < id ? -1 : (arg.id == id ? 0 : 1));

}

}

В данный момент код Individual нас не интересует — достаточно знать, что объект можно создавать с именем или без, и у каждого объекта Individual имеется метод id(), возвращающий уникальный идентификатор. Также имеется метод toString(); если имя не указано,toString() выдает имя типа. Иерархия классов, производных от Individual:

//typeinfo/pets/Person.java

package typeinfo.pets;

public class Person extends Individual {

public Person(String name) { super(name); }

}

//typeinfo/pets/Pet.java

package typeinfo.pets;

public class Pet extends Individual {

public Pet(String name) { super(name); }

public Pet () { super(); }

}

//typeinfo/pets/Dog.java

package typeinfo.pets;

public class Dog extends Pet {

public Dog(String name) { super(name); }

public Dog() { super(); }

}

//typeinfo/pets/Cat.java

package typeinfo.pets;

public class Cat extends Pet {

public Cat(String name) { super(name); }

public Cat() { super();}

}

//typeinfo/pets/Rodent.java

package typeinfo.pets;

public class Rodent extends Pet {

public Rodent(String name) { super(name); }

public Rodent() { super(); }

}

//typeinfo/pets/Pug.java

package typeinfo.pets;

public class Pug extends Dog {

public Pug(String name) { super(name); }

public Pug() { super(); }

}

//typeinfo/pets/Mutt.java

package typeinfo.pets;

public class Mutt extends Dog {

public Mutt(String name) { super(name); }

public Mutt() { super(); }

}

//typeinfo/pets/EgyptianMau.java

package typeinfo.pets;

public class EgyptianMau extends Cat {

public EgyptianMau(String name) { super(name); }

public EgyptianMau() { super(); }

}

//typeinfo/pets/Manx.java

package typeinfo.pets;

public class Manx extends Cat {

public Manx(String name) { super(name); }

public Manx() { super(); }

}

//typeinfo/pets/Cymric.java

package typeinfo.pets;

public class Cymric extends Manx {

public Cymric(String name) { super(name); }

public Cymric() { super(); }

}

//typeinfo/pets/Rat.java

package typeinfo.pets;

public class Rat extends Rodent {

public Rat(String name) { super(name); }

public Rat() { super(); }

}

//typeinfo/pets/Mouse.java

package typeinfo.pets;

public class Mouse extends Rodent {

public Mouse(String name) { super(name); }

public Mouse() { super(); }

}

//typeinfo/pets/Hamster.java

package typeinfo.pets;

public class Hamster extends Rodent {

public Hamster(String name) { super(name); }

public Hamster() { super(); }

}

Затем нам понадобятся средства для создания случайных типов Pet, а для удобства — массивов и списков (List) с элементами Pet. Чтобы этот инструментарий мог «пережить» несколько разных реализаций, мы определим его в виде абстрактного класса:

// typeinfo/pets/PetCreator java

// Создание случайных последовательностей Pet

package typeinfo.pets;

import java.util.*;

 

public abstract class PetCreator {

//инициализация генератора случайных чисел

private Random rand = new Random(47);

//абстрактный метод возвращает список возможных типов(классов) животных

public abstract List<Class<? extends Pet>> getTypes();

 

 

public Pet randomPet(){ // Создание одного случайного объекта Pet

// генерация случайного номера в диапазоне списка классов животных

int n = rand.nextInt(getTypes().size());

try {//создание нового объекта для класса с данным номером в списке классов

return getTypes().get(n).newInstance(); }

catch( InstantiationException e) {throw new RuntimeException(e); }

catch (IllegalAccessException e) {throw new RuntimeException(e);}

}

 

 

public Pet[] createArray(int size) {//создание массива случайных Pet

Pet[] result = new Pet[size];

for (int i=0; i < size; i++)

result[i] = randomPet();

return result;

}

 

public ArrayList<Pet> arrayList(int size) {// создание списка случайных Pet

ArrayList<Pet> result = new ArrayList<Pet>();

Collections.addAll(result, createArray(size));

return result;

}

}

Абстрактный метод getTypes() поручает производному классу получение списка объектов Class. В качестве типа класса указан «любой производный от Pet», поэтому newInstance() создает Pet без необходимости преобразования типа. Метод randomPet() осуществляет случайную выборку из List и использует полученные объекты Class для создания нового экземпляра данного класса вызовом Class.newInstance(). Метод createArray() использует randomPet() для заполнения массива, a arrayList(), в свою очередь, использует createArray(). При вызове newInstance() возможны два вида исключений, обрабатываемые в секциях catch за блоком try. Имена исключений достаточно хорошо объясняют суть проблемы (IllegalAccessException — нарушение механизма безопасности Java, в данном случае если конструктор по умолчанию объявлен private). Определяя субкласс PetCreator, достаточно предоставить список типов Pet, которые должны создаваться с использованием randomPet() и других методов. Метод getTypes() возвращает ссылку на статический объект List. Реализация с использованием forName() выглядит так:

//typeinfo/pets/ForNameCreator.java

package typeinfo.pets;

import java.util.*;

 

public class ForNameCreator extends PetCreator {

 

private static List<Class<? extends Pet>> types = new ArrayList<Class<? extends Pet>>();

private static String[] typeNames = {

"typeinfo.pets.Mutt",

"typeinfo.pets.Pug",

"typeinfo.pets.EgyptianMau",

"typeinfo.pets.Manx",

"typeinfo.pets.Cymric",

"typeinfo.pets.Rat",

"typeinfo.pets.Mouse",

"typeinfo.pets.Hamster"

};

 

@SuppressWarnings("unchecked")

// загрузчик списка классов

private static void loader() {

try { //при загрузке класс приводится к типу заданному типизациии контейнера

for(String name : typeNames)

types.add((Class<? extends Pet>)Class.forName(name));

}

catch(ClassNotFoundException e)

{throw new RuntimeException(e);}

}

// статический блок ОДНОКРТАНО загружающий список при инициализации данного класса

static { loader(); }

 

@Override

public List<Class<? extends Pet>> getTypes(){return types;}

}

Метод loader() создает список List объектов Class с использованием метода Class.forName(). При этом может произойти исключение ClassNotFoundException, что вполне понятно — ведь ему передается строка, содержимое которой невоз­можно проверить на стадии компиляции. При ссылке на эти классы необходимо указывать имя пакета, которому они принадлежат (typeinfo). Для получения типизованного списка объектов Class требуется преобразование типа, что приводит к выдаче предупреждения на стадии компиляции. Методloader() определяется отдельно и размещается в секции статической инициализации, потому что директива @SuppressWarnings не может располагаться прямо в секции статической инициализации. Для подсчета объектов Pet нам понадобится механизм подсчета их разных видов. Для этой цели идеально подойдет карта (Мар), в которой ключами являются имена типов Pet, а значениями — переменные Integer с количеством Pet. Например, это позволит получать ответы на вопросы типа «сколько существует объектов Hamster?». При подсчетеPet будет использоваться ключевое слово instanceof:

//typeinfo/PetCount.java

// Использование instanceof

package typeinfo;

 

import typeinfo.pets.*;

import java.util.*;

import static net.mindview.util.Print.*;

public class PetCount {

 

static class PetCounter extends HashMap<String,Integer> {

public void count(String type) {

Integer quantity = get(type);

if(quantity == null)

put(type, 1);

Else

put(type, quantity +1);

}

}

 

public static void countPets(PetCreator creator) {

PetCounter counter= new PetCounter();

for(Pet pet : creator.createArray(20)) { // Подсчет всех объектов Pet:

printnb(pet.getClass().getSimpleName() + " ");

 

if(pet instanceof Pet) counter.count("Pet");

if(pet instanceof Dog) counter.count("Dog");

if(pet instanceof Mutt) counter.count("Mutt");

if(pet instanceof Pug) counter.count("Pug");

if(pet instanceof Cat) counter.count("Cat");

if(pet instanceof Manx) counter.count("EgyptіanMau");

if(pet instanceof Manx) counter.count("Manx");

if(pet instanceof Manx) counter.count("Cymric");

if(pet instanceof Rodent) counter.count("Rodent");

if(pet instanceof Rat) counter.count("Rat");

if(pet instanceof Mouse) counter.count("Mouse");

if(pet instanceof Hamster) counter.count("Hamster");

}

// Вывод результатов подсчета.

print();

print(counter);

}

public static void main(String[] args) {

//countPets(new ForNameCreator());

countPets(new LiteralPetCreator());

}

}

<spoiler text="Output:">

Rat Manx Cymric Mutt Pug Cymric Pug Manx Cymric Rat EgyptianMau Hamster EgyptianMau Mutt

Mutt Cymric Mouse Pug Mouse Cymric

{Pug=3. Cat=9, Hamster=l, Cymric=7, Mouse=2, Mutt=3, Rodent=5, Pet=20, Manx=7,

EgyptianMau=7, Dog=6, Rat=2}

</spoiler> В countPets массив случайным образом заполняется объектами Pet с использованием PetCreator. Затем каждый объект Pet в массиве тестируется и подсчитывается при помощи instanceof. У ключевого слова instanceof имеется одно серьезное ограничение: объект можно сравнивать только с именованным типом, но не с объектом Class. Возможно, вам показалось, что в предыдущем примере перебор всех выражений instanceof выглядит неудобно и громоздко, и вы правы. Тем не менее автоматизировать этот процесс невозможно — создать из объектов Class список ArrayList и сравнивать объекты по очереди с каждым его элементом не получится (к счастью, сущест­вует альтернативное решение, но это попозже). Впрочем, особенно горевать по этому поводу не стоит — если вам приходится записывать множество проверок instanceof, скорее всего, изъян кроется в архитектуре программы.



<== предыдущая лекция | следующая лекция ==>
Литералы class | Использование литералов class


Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.

Генерация страницы за: 0.19 сек.