Проверка перед приведением типа
Пока Вы видели две формы RTTI включающие:
В C++, классическое приведение “(Shape)” не использует RTTI. В этом случае компилятору сообщается, что объект просто имеет новый тип. В языке Java, который выполняет проверку типа, это приведение часто называется “безопасное нисходящее приведение типа.” Причина использования термина “нисходящее приведение” является историческим соглашением диаграммы иерархии классов. Если приведение типа Circle к типу Shape является восходящим, то приведение типа Shape к типу Circle является нисходящим. Однако Вы знаете, что класс Circle является еще и классом Shape, и компилятор свободно позволяет присвоение с восходящим приведением типа, но Вы не знаете, что класс Shape обязательно является классом Circle, так что компилятор не позволит выполнить присвоение с нисходящим приведением без использования явного приведения.
В Java существует третья форма RTTI. Это ключевое слово instanceof которое говорит Вам, что объект является экземпляром конкретного типа. Оно возвращает значение boolean, так, что Вы используете его в форме вопроса следующим образом:
if(x instanceof Dog) ((Dog)x).bark();
Приведенное выше выражение if проверяет, является ли объект x экземпляром класса Dog перед приведением объекта x к типу Dog. Это важно - использовать instanceof перед нисходящим приведением, когда у Вас нет ничего, что могло бы дать информацию о типе объекта; в противном случае приведение может завершится выбросом исключения ClassCastException.
Обычно, Вы можете искать один типом (например, треугольниками, чтобы окрасить их в пурпурный), но Вы можете просто повесить ярлычки на все объекты используя instanceof. Представьте, что у Вас есть группа классов Pet:
//: c12:Pets.java class Pet {} class Dog extends Pet {} class Pug extends Dog {} class Cat extends Pet {} class Rodent extends Pet {} class Gerbil extends Rodent {} class Hamster extends Rodent {}
class Counter { int i; } ///:~
Класс Counter используется для хранения количества любых классов типа Pet. Вы можете считать, что это переменная Integer которая может быть изменена.
Используя instanceof, все классы Pet могут быть подсчитаны:
//: c12:PetCount.java // Использование instanceof. import java.util.*;
public class PetCount { static String[] typenames = { "Pet", "Dog", "Pug", "Cat", "Rodent", "Gerbil", "Hamster", }; // Исключение выбрасывается на консоль: public static void main(String[] args) throws Exception { ArrayList pets = new ArrayList(); try { Class[] petTypes = { Class.forName("Dog"), Class.forName("Pug"), Class.forName("Cat"), Class.forName("Rodent"), Class.forName("Gerbil"), Class.forName("Hamster"), }; for(int i = 0; i < 15; i++) pets.add( petTypes[ (int)(Math.random()*petTypes.length)] .newInstance()); } catch(InstantiationException e) { System.err.println("Cannot instantiate"); throw e; } catch(IllegalAccessException e) { System.err.println("Cannot access"); throw e; } catch(ClassNotFoundException e) { System.err.println("Cannot find class"); throw e; } HashMap h = new HashMap(); for(int i = 0; i < typenames.length; i++) h.put(typenames[i], new Counter()); for(int i = 0; i < pets.size(); i++) { Object o = pets.get(i); if(o instanceof Pet) ((Counter)h.get("Pet")).i++; if(o instanceof Dog) ((Counter)h.get("Dog")).i++; if(o instanceof Pug) ((Counter)h.get("Pug")).i++; if(o instanceof Cat) ((Counter)h.get("Cat")).i++; if(o instanceof Rodent) ((Counter)h.get("Rodent")).i++; if(o instanceof Gerbil) ((Counter)h.get("Gerbil")).i++; if(o instanceof Hamster) ((Counter)h.get("Hamster")).i++; } for(int i = 0; i < pets.size(); i++) System.out.println(pets.get(i).getClass()); for(int i = 0; i < typenames.length; i++) System.out.println( typenames[i] + " quantity: " + ((Counter)h.get(typenames[i])).i); } } ///:~
Существуют некоторые ограничения на использование instanceof: Вы можете сравнивать только именованные типы, но не объекты Class. В примере, приведенном выше, Вам может показаться, что это довольно скучно набирать все выражения instanceof, и Вы будете правы. Но не существует способа для правильной автоматизации instanceof созданием массива ArrayList объектов Class и сравнения их. Это не такое сильное ограничение, как Вы можете представить, т.к. Вы, в конечном счете, поймете, что Ваш замысел не будет осуществлен, если Вы прекратите писать множество этих выражений instanceof.
Конечно, этот пример был придуман, Вы, возможно, будете размещать статический член данных в каждом типе и увеличивать его в конструкторе, чтобы сохранить их количество. Вы вполне можете сделать что-то в этом духе, если у Вас есть исходные тексты класса и Вы можете менять их.. Но т.к. это бывает не всегда, Вы можете просто использовать RTTI.