In computing, type introspection is a capability of some object-oriented programming languages to determine the type of an object at runtime. This is a notable capability of the Objective-C language, and is a common feature in any language that allows object classes to be manipulated as first-class objects by the programmer. Type introspection can be used to implement polymorphism.
Contents |
Type introspection is a core feature of Ruby. In Ruby, the Object class (ancestor of every class) provides Object#instance_of? and Object#kind_of? methods for checking the instance's class. The latter returns true when the particular instance the message was sent to is an instance of a descendant of the class in question. To clarify, consider the following example code (you can immediately try this with irb):
$ irb irb(main):001:0> A=Class.new => A irb(main):002:0> B=Class.new A => B irb(main):003:0> a=A.new => #<A:0x2e44b78> irb(main):004:0> b=B.send 'new' => #<B:0x2e431b0> irb(main):005:0> a.instance_of? A => true irb(main):006:0> b.instance_of? A => false irb(main):007:0> b.kind_of? A => true
In the example above, the Class class is used as any other class in Ruby. Two classes are created, A and B, the former is being a superclass of the latter, then one instance of each class is checked. The last expression gives true because A is a superclass of the class of b. The example below shows an alternative method in Ruby that can be used to define classes (and leads to the same result):
$ irb irb(main):001:0> class A; end => nil irb(main):002:0> class B < A; end => nil irb(main):003:0> a=A.new => #<A:0x2e4c33c> irb(main):004:0> b=B.new => #<B:0x2e4a974> irb(main):005:0> a.instance_of? A => true irb(main):006:0> b.instance_of? A => false irb(main):007:0> b.kind_of? A => true
Or you can directly ask for the class of any object, and "compare" them (code below assumes having executed the code above):
irb(main):008:0> A.instance_of? Class => true irb(main):009:0> a.class => A irb(main):010:0> a.class.class => Class irb(main):011:0> A > B => true irb(main):012:0> B <= A => true
In Objective-C, for example, both the generic Object and NSObject (in Cocoa/OpenStep) provide the method isMemberOfClass: which returns true if the argument to the method is an instance of the specified class. The method isKindOfClass: analogously returns true if the argument inherits from the specified class.
For example, say we have a Puppy and Kitten class inheriting from Animal, and a Vet class.
Now, in the desex method we can write
- desex: (id) to_desex { if([to_desex isKindOfClass:[Animal class]]) { //we're actually desexing an Animal, so continue if([to_desex isMemberOfClass:[Puppy class]]) desex_dog(to_desex); else if([to_desex isMemberOfClass:[Kitten class]]) desex_cat(to_desex); else error(); } else { error(); } }
Now, when desex is called with a generic object (an id), the function will behave correctly depending on the type of the generic object.
C++ supports type introspection via the typeid and dynamic_cast keywords. The dynamic_cast
expression can be used to determine whether a particular object is of a particular derived class. For instance:
if (Person *p = dynamic_cast<Person *>(obj)) { p->walk(); }
The typeid
operator retrieves a std::type_info
object describing the most derived type of an object:
if (typeid(Person) == typeid(*obj)) { serialize_person( obj ); }
Type introspection has been a part of Object Pascal since the original release of Delphi, which uses RTTI heavily for visual form design. In Object Pascal, all classes descend from the base TObject class, which implements basic RTTI functionality. Every class's name can be referenced in code for RTTI purposes; the class name identifier is implemented as a pointer to the class's metadata, which can be declared and used as a variable of type TClass. The language includes an is operator, to determine if an object is or descends from a given class, an as operator, providing a type-checked typecast, and several TObject methods.
procedure Form1.MyButtonOnClick(Sender: TObject); var aButton: TButton; SenderClass: TClass; begin SenderClass := Sender.ClassType; //returns Sender's class pointer if sender is TButton then begin aButton := sender as TButton; EditBox.Text := aButton.Caption; //Property that the button has but generic objects don't end else begin EditBox.Text := Sender.ClassName; //returns the name of Sender's class as a string end; end;
The simplest example of type introspection in Java is the instanceof
[1] operator. The instanceof
operator determines whether a particular object belongs to a particular class (or a subclass of that class, or a class that implements that interface). For instance:
if(obj instanceof Person){ Person p = (Person)obj; p.walk(); }
The java.lang.Class
[2] class is the basis of more advanced introspection.
For instance, if it is desirable to determine the actual class of an object (rather than whether it is a member of a particular class), Object.getClass()
and Class.getName()
can be used:
System.out.println(obj.getClass().getName());
In PHP introspection can be done using instanceof
operator. For instance:
if ($obj instanceof Person) { // Do whatever you want }
Introspection can be achieved using the ref
and isa
functions in Perl.
We can introspect the following classes and their corresponding instances:
package Animal; sub new { my $class = shift; return bless {}, $class; } package Dog; use base 'Animal'; package main; my $animal = Animal->new(); my $dog = Dog->new();
using:
print "This is an Animal.\n" if ref $animal eq 'Animal'; print "Dog is an Animal.\n" if $dog->isa('Animal');
Much more powerful introspection in Perl can be achieved using the Moose object system[3] and the Class::MOP
meta-object protocol,[4] for example this is how you can check if a given object does a role X
:
if ($object->meta->does_role("X")) { # do something ... }
This is how you can list fully qualified names of all of the methods that can be invoked on the object, together with the classes in which they were defined:
for my $method ($object->meta->get_all_methods) { print $method->fully_qualified_name, "\n"; }
The most common method of introspection in Python is using the dir
function to detail the attributes of an object. For example:
class foo(object): def __init__(self, val): self.x = val def bar(self): return self.x ... >>> dir(foo(5)) ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', 'bar', 'x']
Also, the built-in functions isinstance
and hasattr
can be used to determine what an object is and what an object does respectively. For example:
>>> a = foo(10) >>> isinstance(a, foo) True >>> hasattr(a, 'bar') True
In Actionscript the function flash.utils.getQualifiedClassName can be used to retrieve the Class/Type name of an arbitrary Object.
// all classes used in as3 must be imported explicitly import flash.utils.getQualifiedClassName; import flash.display.Sprite; // trace is like System.print.out in Java or echo in PHP trace(flash.utils.getQualifiedClassName("I'm am a String")); // "String" trace(flash.utils.getQualifiedClassName(1)); // "int", see dynamic casting for why not Number trace(flash.utils.getQualifiedClassName(new flash.display.Sprite())); // "flash.display.Sprite"
Or alternatively in actionscipt the operator is can be used to determine if an object is of a specific type
// trace is like System.print.out in Java or echo in PHP trace("I'm a String" is String); // true trace(1 is String); // false trace("I'm am a String" is Number); // false trace(1 is Number); // true
This second function can be used to test class inheritance parents as well
import flash.display.DisplayObject; import flash.display.Sprite; // extends DisplayObject trace(new flash.display.Sprite() is flash.display.Sprite); // true trace(new flash.display.Sprite() is flash.display.DisplayObject); // true, because Sprite extends DsiplayObject trace(new flash.display.Sprite() is String); // false
Like perl, actionscript can go further than getting the Class Name, but all the metadata, functions and other elements that make up an object using the flash.utils.describeType function, this is used when implementing reflection in actionscript.
import flash.utils.describeType; import flash.utils.getDefinitionByName; import flash.utils.getQualifiedClassName; import flash.display.Sprite; var className:String = getQualifiedClassName(new flash.display.Sprite()); // "flash.display.Sprite" var classRef:Class = getDefinitionByName(className); // Class reference to flash.display.Sprite // eg. 'new classRef()' same as 'new flash.display.Sprite()' trace(describeType(classRef)); // return XML object describing type // same as : trace(describeType(flash.display.Sprite));