Reflection & Introspection
A program examining and modifying its own structure at runtime.
Overview
Reflection is a program's ability to examine and modify its own structure and behaviour at runtime. Introspection is the read-only subset, inspecting types, methods, and fields without changing them. Together they power debugging tools, serialisers, dependency injection containers, and testing frameworks.
Origin
Reflection was formalized in Brian Cantwell Smith's 1982 PhD thesis on procedural reflection. Smalltalk implemented a full reflective system in the 1970s. Java's java.lang.reflect package (1.1, 1997) brought it to mainstream enterprise programming.
Examples
Introspection in Ruby
class Order
attr_accessor :id, :status, :total
def complete!
self.status = 'completed'
end
end
order = Order.new
order.class # => Order
order.class.ancestors # => [Order, Object, Kernel, BasicObject]
Order.instance_methods(false) # => [:id, :id=, :status, :status=, :total, :total=, :complete!]
order.respond_to?(:complete!) # => true
# Serialise all attributes dynamically
Order.instance_methods(false)
.select { |m| !m.to_s.end_with?('=') }
.each_with_object({}) { |m, h| h[m] = order.send(m) }JavaScript runtime type inspection
function inspect(obj) {
const proto = Object.getPrototypeOf(obj)
const ownKeys = Object.getOwnPropertyNames(obj)
const protoKeys = Object.getOwnPropertyNames(proto)
.filter(k => k !== 'constructor')
return {
constructor: obj.constructor.name,
ownProperties: ownKeys,
methods: protoKeys,
isExtensible: Object.isExtensible(obj),
}
}
// TypeScript reflect-metadata for DI containers
import 'reflect-metadata'
@Injectable()
class UserService {
constructor(private repo: UserRepository) {}
}
const paramTypes = Reflect.getMetadata('design:paramtypes', UserService)
// => [UserRepository], the DI container uses this to inject the depUse Cases
- 01Dependency injection containers discovering constructor parameter types
- 02Serialisers mapping object fields to JSON/XML without explicit field lists
- 03Test frameworks inspecting objects to generate assertions or mocks
- 04Admin panels and ORMs that generate forms from model schemas
- 05Plugin systems dynamically loading and inspecting extension classes
When Not to Use
- //When the structure is known at compile time, explicit code is faster and easier to reason about
- //In performance-critical hot paths: reflection bypasses compiler optimisations
- //When it enables callers to reach private internals, prefer explicit public APIs
Technical Notes
- Reflection breaks encapsulation by design, it is a power tool, not default practice
- TypeScript's reflect-metadata and emitDecoratorMetadata enable type-safe reflection in compiled output
- Ruby's send bypasses method visibility (send :private_method). Use public_send to respect visibility
- In Java, reflection calls are significantly slower than direct method calls. JIT can inline direct calls but cannot predict reflected calls
More in Programming Techniques