1. Overview

In programming, we may have come across the terms introspection and reflection. Introspection and reflection are the abilities of a system to inspect and modify itself at runtime, respectively. These two terms are somewhat related but there are a few differences.

In this article, we’ll learn the differences between introspection and reflection. We’ll go through different code examples to better grasp the terms.

2. Introspection

In software development, introspection is a process through which a program examines its own structure at runtime. Specifically, it focuses on inspecting the types and properties of objects. For instance, we can use introspection to find out the class of an object, aggregate the object properties and methods, and determine its parent class.

However, that’s as far as introspection goes. It doesn’t modify the behavior of the objects. Its sole purpose is to provide information about the code, without modifying it.

There are a lot of programming languages that support introspection such as Python, Ruby, JavaScript, and so on. Typically, we make heavy use of introspection in dynamically typed languages because the types might not be known at compile time. Let’s take a look at an example in JavaScript:

function CreatePerson(name) {
    return new Person(name)
}

In the code, the function CreatePerson expects a parameter of type string. The JavaScript interpreter doesn’t check the type of name. So, we can pass any type to the function, which may cause errors at runtime. On that account, we often resort to introspection in the userland code to remedy such errors.

In contrast, in statically typed languages, such as Java, the types are validated by the compiler. Therefore, we rarely rely on introspection because types are enforced by the compiler.

However, we still need it for very specific scenarios. For instance, introspection is heavily used in Object-Relational Mapping (ORM) frameworks such as Hibernate.

In the next section, we’ll look at some examples of introspection in Python.

2.1. Introspection in Action

Let’s write a simple Python code that makes use of introspection:

>>> type(2)
<class 'int'>
>>> type("baeldung")
<class 'str'>

In the example, we use the type() built-in to determine the type of the given values. Similarly, we can also use isinstance() to verify a type:

>>> class Webpage:
...     pass
... 
>>> page = Webpage()
>>> type(page)
<class '__main__.Webpage'>
>>> isinstance(page, Webpage)
True

In addition, we can list all the properties and methods of an object as well:

class Webpage:

  def __init__(self, path):
    self.path = path


page = Webpage("index.html")
print(dir(page))

Upon running, it prints out a list that contains the properties and methods of page:

$ python introspect.py
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'path']

Notably, the last item in the list is path that we’ve defined in the class. Moreover, we can get more control over this by using the getattr() and hasattr() built-in functions:

>>> class Webpage:
...     def __init__(self, path):
...             self.path = path
... 
>>> page = Webpage("index.html")
>>> hasattr(page, "path")
True
>>> getattr(page, "path")
'index.html'
>>> getattr(page, "page_type", "xml")
'xml'

In the last statement, we call getattr() to return the value of the page_type attribute. Evidently, it’s not present in the class. Ergo, we provided the third argument “xml” that will be returned as the default value.

Furthermore, there’s an inspect module that provides useful methods to get information about objects such as classes, functions, modules, and tracebacks.

3. Reflection

Reflection is the ability of a program to examine as well as modify itself. Therefore, we can say that introspection is a part of reflection. However, reflection goes one step beyond and modifies the structure of a program at runtime. This process is known as intercession.

Reflection provides a way to create or destroy instances of a class, call methods, and access fields of an object dynamically. In addition, reflection is also used to intercept method calls via dynamic proxies.

In software development, there are Dependency Injection (DI) frameworks, like Spring, that make heavy use of reflection. For instance, in Spring, we delegate the responsibility of managing instances of classes to an IoC container such as the Spring Container.

Let’s take a look at an example to demonstrate how reflection is used in Java. We’ll create a simple class, which has a single private property:

public class Person {
    private String name = "John Doe";
}

In the code, the name field is private. Private fields in a class are meant to be private as intended by the original developer. However, we often need to break this rule in scenarios such as testing. In addition, the class doesn’t provide a setter method that can modify the value.

However, with reflection, we can modify it without a setter:

public class Main {
    public static void main(String[] args) {
        try {
            Person person = new Person();
            Field field = MyClass.class.getDeclaredField("name");
            field.setAccessible(true);
            field.set(obj, "Joe Blow");
            String fieldValue = (String) field.get(obj);
            System.out.println("New name: " + fieldValue);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

In the code, we used the reflect package that provides reflection capabilities in Java. Then, in the main method, we created an instance of Person and modified its name property through the field object.

4. Summary

The following table outlines the main differences between introspection and reflection:

Introspection

Reflection

Inspects the structure of a program at runtime

Modifies the structure and behavior of a program at runtime

Used in logging, debugging, and dynamic type-checking

Used in dependency injection, dynamic proxies, and serialization

Low impact on performance

High impact on performance

Safe to use because no modifications are involved

High risk of introducing bugs and security risks

5. Conclusion

In this article, we learned the differences between introspection and reflection. We learned the uses of introspection and reflection in different scenarios. In addition, we also went through several code examples in Python and Java.