В программировании на основе классов понижение типа или уточнение типа — это процесс приведения ссылки на базовый или родительский класс к более ограниченной ссылке на производный класс . [1] Это допустимо только в том случае, если объект уже является экземпляром производного класса, и поэтому это преобразование по своей сути подвержено ошибкам.
Во многих средах интроспекция типов может использоваться для получения типа экземпляра объекта во время выполнения, а затем использовать этот результат для явной оценки его совместимости типов с другим типом. Возможные результаты сравнения полиморфных типов — помимо того, что они эквивалентны (идентичны) или не связаны (несовместимы) — включают два дополнительных случая: а именно, когда первый тип выводится из второго, а затем то же самое, но поменянное наоборот (см.: Подтипирование § Подчинение ).
Используя эту информацию, программа может проверить, прежде чем выполнять операцию, например, сохранение объекта в типизированной переменной, является ли эта операция безопасной по типу или приведет к ошибке. Если тип экземпляра среды выполнения является производным от типа целевой переменной (и, следовательно, родительской), то возможно понижение типа.
Некоторые языки, такие как OCaml , запрещают приведение типов. [2]
открытый класс Fruit {} // родительский класс открытый класс Apple расширяет Fruit {} // дочерний класс public static void main ( String [] args ) { // Ниже приведено неявное приведение к базовому типу: Fruit parent = new Apple (); // Ниже приведено приведение к базовому типу. Здесь это работает, поскольку переменная `parent` // содержит экземпляр Apple: Apple child = ( Apple ) parent ; }
// Родительский класс: class Fruit { public : // Должен быть полиморфным для использования динамического приведения типов, проверяемого во время выполнения. virtual ~ Fruit () = default ; }; // Дочерний класс: class Apple : public Fruit {}; int main ( int argc , const char ** argv ) { // Ниже приведено неявное приведение к базовому типу: Fruit * parent = new Apple (); // Ниже приведено приведение к базовому типу. Здесь это работает, поскольку переменная `parent` // содержит экземпляр Apple: Apple * child = dynamic_cast < Apple *> ( parent ); }
Приведение типа вниз полезно, когда тип значения, на которое ссылается переменная Parent, известен и часто используется при передаче значения в качестве параметра. В приведенном ниже примере метод objectToString принимает параметр Object, который, как предполагается, имеет тип String.
public static String objectToString ( Object myObject ) { // Это будет работать только в том случае, если myObject, в данный момент содержащий значение, является string. return ( String ) myObject ; } public static void main ( String [] args ) { // Это сработает, так как мы передали String, поэтому myObject имеет значение String. String result = objectToString ( "My String" ); Object iFail = new Object (); // Это не сработает, так как мы передали Object, который не имеет значения String. result = objectToString ( iFail ); }
При таком подходе приведение к нижнему типу не позволяет компилятору обнаружить возможную ошибку и вместо этого вызывает ошибку во время выполнения. Приведение к нижнему типу myObject к String ('(String)myObject') было невозможно во время компиляции, поскольку бывают случаи, когда myObject имеет тип String, поэтому только во время выполнения мы можем выяснить, является ли переданный параметр логическим. Хотя мы также могли бы преобразовать myObject в String во время компиляции с помощью универсального java.lang.Object.toString(), это могло бы привести к вызову реализации toString() по умолчанию, где она была бы бесполезной или небезопасной, а обработка исключений не могла бы этого предотвратить.
В C++ проверка типов во время выполнения реализована с помощью dynamic_cast . Понижение типов во время компиляции реализовано с помощью static_cast , но эта операция не выполняет проверку типов. При неправильном использовании она может привести к неопределенному поведению.
Популярным примером плохо продуманного дизайна являются контейнеры основных типов , [ требуется ссылка ] такие как контейнеры Java до появления дженериков Java , которые требуют приведения содержащихся объектов к более низкому типу, чтобы их можно было использовать снова.