Из-за того, что Вы теряете информацию о типе при приведении к базовому типу (движение вверх по диаграмме наследования), то важно получить тип полученной информации, для этого нужно двигаться назад, вниз по иерархии, используя тем самым приведение к дочернему типу. Однако как Вы знаете приведение к базовому типу безопасно всегда, базовый класс не может иметь больший, интерфейс чем дочерний, поэтому каждое сообщение, которое Вы посылаете через базовый класс гарантированно будет получено. Но при приведении к дочернему типу, Вы в действительности не знаете, что шейп к примеру в действительности круг. А он может быть и треугольником и квадратом или чем еще угодно.
Для разрешения этой проблемы должен существовать некоторый путь, гарантирующий, что приведение к дочернему типу корректно, поскольку Вы наверняка не хотите привести тип к неверному значению и послать сообщение объекту, который не сможет его принять. Это может быть несколько не безопасно.
В некоторых языках (типа C++) Вы должны осуществлять специальную операцию в получении типо-безопасного приведения к дочернему типу, но в Java любое приведение к типу проверяется! И как бы это не выглядело странно, Вы просто выполняете ординарное родительское приведение, во время работы, это приведение проверяется, для того, что бы убедиться, что это на самом деле то, что нужно. Если что-то не так, то Вы получите ClassCastException. Этот акт проверки типов во время работы называется идентификация типов во время работы (run-time type identification (RTTI)). Следующий пример демонстрирует поведение RTTI:
//: c07:RTTI.java
// Приведение к дочернему типу и RTTI.
import java.util.*;
class Useful { public void f() {} public void g() {} }
class MoreUseful extends Useful { public void f() {} public void g() {} public void u() {} public void v() {} public void w() {} }
public class RTTI { public static void main(String[] args) { Useful[] x = { new Useful(), new MoreUseful() }; x[0].f(); x[1].g(); // Время компиляции: метод не найден в Useful: