В вычислительной технике интроспекция типа — это способность программы исследовать тип или свойства объекта во время выполнения . Некоторые языки программирования обладают этой возможностью.
Интроспекцию не следует путать с рефлексией , которая идет на шаг дальше и является способностью программы манипулировать метаданными, свойствами и функциями объекта во время выполнения. Некоторые языки программирования также обладают этой возможностью (например, Java , Python , Julia и Go ).
В Objective-C , например, как универсальный Object, так и NSObject (в Cocoa / OpenStep ) предоставляют метод isMemberOfClass:
, который возвращает true, если аргумент метода является экземпляром указанного класса. Метод isKindOfClass:
аналогично возвращает true, если аргумент наследуется от указанного класса.
Например, предположим, что у нас есть Apple
и Orange
класс, унаследованный от Fruit
.
Теперь в eat
методе мы можем записать
- ( void ) eat: ( id ) sth { if ([ sth isKindOfClass : [ Fruit class ]]) { // на самом деле мы едим Fruit, поэтому продолжаем if ([ sth isMemberOfClass : [ Apple class ]]) { eatApple ( sth ); } else if ([ sth isMemberOfClass : [ Orange class ]]) { eatOrange ( sth ); } else { error (); } } else { error (); } }
Теперь, когда eat
вызывается с универсальным объектом (an id
), функция будет вести себя правильно в зависимости от типа универсального объекта.
C++ поддерживает интроспекцию типа через ключевые слова typeid и dynamic_cast информации о типе времени выполнения (RTTI) . Выражение может использоваться для определения, принадлежит ли конкретный объект к определенному производному классу. Например:dynamic_cast
Человек * p = dynamic_cast < Человек *> ( obj ); если ( p != nullptr ) { p -> walk (); }
Оператор typeid
извлекает std::type_info
объект, описывающий наиболее производный тип объекта:
если ( typeid ( Person ) == typeid ( * obj )) { serialize_person ( obj ); }
Тип интроспекции был частью Object Pascal с момента первоначального выпуска Delphi, который активно использует RTTI для визуального проектирования форм. В Object Pascal все классы происходят от базового класса TObject, который реализует базовую функциональность RTTI. Имя каждого класса может быть указано в коде для целей RTTI; идентификатор имени класса реализован как указатель на метаданные класса, которые могут быть объявлены и использованы как переменная типа TClass. Язык включает оператор is для определения того, является ли объект или происходит от данного класса, оператор as , обеспечивающий приведение типа с проверкой типа, и несколько методов TObject. Более глубокая интроспекция (перечисление полей и методов) традиционно поддерживается только для объектов, объявленных в состоянии $M+ (прагма), обычно TPersistent, и только для символов, определенных в разделе published. Delphi 2010 увеличил это почти до всех символов.
procedure Form1.MyButtonOnClick ( Sender : TObject ) ; var aButton : TButton ; SenderClass : TClass ; begin SenderClass := Sender.ClassType ; // возвращает указатель класса отправителя , если отправитель — TButton , то begin aButton : = sender as TButton ; EditBox.Text : = aButton.Caption ; //Свойство , которое есть у кнопки , но универсальные объекты не заканчиваются else begin EditBox.Text := Sender.ClassName ; //возвращает имя класса отправителя в виде строки end ; end ;
Простейшим примером интроспекции типа в Java является оператор instanceof
[1] . instanceof
Оператор определяет, принадлежит ли конкретный объект конкретному классу (или подклассу этого класса, или классу, реализующему этот интерфейс). Например:
если ( obj instanceof Person ) { Person p = ( Person ) obj ; p . walk (); }
Класс java.lang.Class
[2] является основой более продвинутой интроспекции.
Например, если желательно определить фактический класс объекта (а не является ли он членом определенного класса ), Object.getClass()
можно Class.getName()
использовать:
System.out.println ( obj.getClass ( ) . getName ( ) ) ;
В PHP интроспекцию можно выполнить с помощью instanceof
оператора. Например:
если ( $obj instanceof Person ) { // Делай что хочешь }
Самоанализ можно осуществить с помощью функций ref
и isa
в Perl .
Мы можем рассмотреть следующие классы и соответствующие им экземпляры:
пакет Животное ; под новый { мой $класс = сдвиг ; возврат благословения {}, $класс ; } пакет Собака ; использовать базу 'Животное' ; пакет main ; мой $animal = Животное -> новый (); мой $dog = Собака -> новый ();
с использованием:
print "Это животное.\n" if ref $animal eq 'Животное' ; print "Собака - животное.\n" if $dog -> isa ( 'Животное' );
Гораздо более мощную интроспекцию в Perl можно осуществить с помощью объектной системы Moose [3] и протокола Class::MOP
метаобъектов [4] ; например, можно проверить, выполняет ли заданный объект роль X :
если ( $object -> meta -> does_role ( "X" )) { # сделать что-то ... }
Вот как можно перечислить полные имена всех методов, которые могут быть вызваны для объекта, вместе с классами, в которых они были определены:
для моего $method ( $object -> meta -> get_all_methods ) { print $method -> fully_qualified_name , "\n" ; }
Наиболее распространенный метод интроспекции в Python — использование dir
функции для детализации атрибутов объекта. Например:
класс Foo : def __init__ ( self , val ) : self.x = val def bar ( self ): вернуть self . x
>>> dir ( Foo ( 5 )) ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', 'bar', 'x']
Также встроенные функции type
и isinstance
могут использоваться для определения того, что представляет собой объект, в то время как может определять, что делаетhasattr
объект . Например:
>>> a = Foo ( 10 ) >>> b = Bar ( 11 ) >>> type ( a ) <type 'Foo'> >>> isinstance ( a , Foo ) True >>> isinstance ( a , type ( a )) True >>> isinstance ( a , type ( b )) False >>> hasattr ( a , 'bar' ) True
Тип интроспекции является основной функцией Ruby . В Ruby класс Object (предок каждого класса) предоставляет Object#instance_of?
и Object#kind_of?
методы для проверки класса экземпляра. Последний возвращает true, когда конкретный экземпляр, которому было отправлено сообщение, является экземпляром потомка рассматриваемого класса. Например, рассмотрим следующий пример кода (вы можете немедленно попробовать его с помощью Interactive Ruby Shell ):
$ irb irb(main):001:0> A = Класс . новый => A irb(main):002:0> B = Класс . новый A => B irb(main):003:0> a = A. новый = > #<A:0x2e44b78> irb(main):004:0> b = B. новый => #<B:0x2e431b0> irb(main):005:0> a . экземпляр_из? A = > истина irb(main):006:0> b . экземпляр_из? A => ложь irb(main):007:0> b . вид_из? A => истина
В приведенном выше примере Class
класс используется как любой другой класс в Ruby. Создаются два класса, A
и B
первый является суперклассом последнего, затем проверяется один экземпляр каждого класса. Последнее выражение дает true, поскольку A
является суперклассом класса b
.
Кроме того, вы можете напрямую запросить класс любого объекта и «сравнить» их (код ниже предполагает выполнение кода выше):
irb(main):008:0> A . instance_of? Класс => true irb(main):009:0> a . class => A irb(main):010:0> a . class . class => Класс irb(main):011:0> A > B => true irb(main):012:0> B <= A => true
В ActionScript (as3) функцию flash.utils.getQualifiedClassName
можно использовать для получения имени класса/типа произвольного объекта.
// все классы, используемые в as3, должны быть импортированы явно import flash.utils.getQualifiedClassName ; import flash.display.Sprite ; // trace похож на System.out.println в Java или echo в PHP trace ( flash.utils.getQualifiedClassName ( " I 'm a String" ) ); // " String " trace ( flash.utils.getQualifiedClassName ( 1 )); // "int" , см . динамическое приведение типов , чтобы узнать , почему нет Number trace ( flash.utils.getQualifiedClassName ( new flash.display.Sprite ( ) ) ) ; // " flash.display.Sprite "
В качестве альтернативы оператор is
можно использовать для определения, относится ли объект к определенному типу:
// трассировка похожа на System.out.println в Java или echo в PHP трассировка ( "I'm a String" is String ); // истина трассировка ( 1 is String ); // ложь трассировка ( "I'm a String" is Number ); // ложь трассировка ( 1 is Number ); // истина
Эту вторую функцию можно использовать также для проверки родительских элементов наследования классов :
import flash.display.DisplayObject ; import flash.display.Sprite ; // расширяет DisplayObject trace ( new flash.display.Sprite ( ) is flash.display.Sprite ) ; // true trace ( new flash.display.Sprite ( ) is flash.display.DisplayObject ) ; // true , потому что Sprite расширяет DisplayObject trace ( new flash.display.Sprite ( ) is String ) ; // false
Как и Perl, ActionScript может не только получать имя класса, но и все метаданные, функции и другие элементы, составляющие объект, используя flash.utils.describeType
функцию; это используется при реализации отражения в ActionScript.
импорт flash.utils.describeType ; импорт flash.utils.getDefinitionByName ; импорт flash.utils.getQualifiedClassName ; импорт flash.display.Sprite ; var className : String = getQualifiedClassName ( new flash . display . Sprite ()); // "flash.display.Sprite" var classRef : Class = getDefinitionByName ( className ); // Ссылка на класс flash.display{{Не опечатка|.}}Sprite // например, 'new classRef()' то же самое, что и 'new flash.display.Sprite()' trace ( describeType ( classRef )); // возвращает объект XML, описывающий тип // то же самое, что и: trace(describeType(flash.display.Sprite));