Философия Java

         

Приведение к дочернему типу и идентификация типов во время работы


Из-за того, что Вы теряете информацию о типе при приведении к базовому типу (движение вверх по диаграмме наследования), то важно получить тип полученной информации, для этого нужно двигаться назад, вниз по иерархии, используя тем самым приведение к дочернему типу. Однако как Вы знаете приведение к базовому типу безопасно всегда, базовый класс не может иметь больший, интерфейс чем дочерний, поэтому каждое сообщение, которое Вы посылаете через базовый класс гарантированно будет получено. Но при приведении к дочернему типу, Вы в действительности не знаете, что шейп к примеру в действительности круг. А он может быть и треугольником и квадратом или чем еще угодно.


Для разрешения этой проблемы должен существовать некоторый путь, гарантирующий, что приведение к дочернему типу корректно, поскольку Вы наверняка не хотите привести тип к неверному значению и послать сообщение объекту, который не сможет его принять. Это может быть несколько не безопасно.

В некоторых языках (типа 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:


//! x[1].u();

((MoreUseful)x[1]).u(); // Приведение к дочернему типу RTTI

((MoreUseful)x[0]).u(); // Обработка исключения

} } ///:~

Как и на диаграмме MoreUseful

расширяет интерфейс Useful. Но поскольку он наследованный, он так же может быть приведен к базовому типу, к Useful. Как Вы можете видеть это происходит в момент инициализации массива x в main( ). Поскольку оба объекта в массиве есть типы от класса Useful, то Вы можете послать методы f( ) и g( ) обоим, а если Вы попытаетесь вызвать u( ) (который существует только в MoreUseful), то Вы получите ошибку времени компиляции.

Если Вы хотите получить доступ к расширенному интерфейсу объекта MoreUseful, Вы можете попытаться привести его к дочернему типу. Если он правильного типа, то все пройдет нормально. В противном случае, Вы получите ClassCastException. Вам не нужно писать какой либо специальный код для этого исключения, поскольку оно сигнализирует об ошибке программиста, которая произошла где-то в программе.

У RTTI есть больше применений, чем простое приведение. К примеру, существует возможность увидеть с каким типом Вы работаете, до того, как Вы попытаетесь привести к дочернему типу. Вся глава 12 посвящена изучению различных аспектов применения идентификации типов во время работы в Java


Содержание раздела