Reflection Basics

Basic Java
≈ 2 min read

Class — a runtime description of a type

Ordinary Java code usually works with objects and calls methods known in advance. Reflection is useful when a program needs to inspect the type itself at runtime: find its name, superclass, interfaces, annotations, or available members. The entry point is a Class<?> object.

Class<String> type = String.class;

System.out.println(type.getName());
System.out.println(type.getSimpleName());

A Class object is not an instance of String. It represents the description of the String type itself in the running JVM.

How to obtain Class

When the type is known in code, the simplest option is the .class literal. If you already have an object, its runtime type can be obtained through getClass().

String text = "hello";

Class<String> first = String.class;
Class<? extends String> second = text.getClass();

When a class name arrives as a string at runtime, Class.forName() is available. This form appears in infrastructure code where the concrete type is not known to the source code in advance.

Class<?> type = Class.forName("java.util.ArrayList");
System.out.println(type.getSimpleName());

What can be learned about a type

Through Class, you can check whether the represented type is an interface, enum, record, or array, obtain its package, superclass, and implemented interfaces, and then move on to fields, methods, constructors, and annotations. We will examine these groups separately so that reflection does not turn into a list of API methods.

Reflection is not meant for ordinary calls. If a concrete type and method are known at compile time, a direct call is usually simpler, clearer, and safer. Reflection is useful specifically when the structure is selected or inspected dynamically.