Retrieving Classes
The entry point for all reflection operations is java.lang.Class. Aside from java.lang.reflect.ReflectPermission, none of the classes in java.lang.reflect have public constructors. To get to these classes, it is necessary to invoke appropriate methods on Class. There are several ways to get a Class depending on whether the code has access to an object, the name of class, a type, or an existing Class.
Every type is either a reference or a primitive. Classes, enums, records, lambdas, and arrays (which all inherit from java.lang.Object) as well as interfaces are all reference types. Examples of reference types include java.lang.String, all the wrapper classes for primitive types such as java.lang.Double, the interface java.io.Serializable, and the enum java.nio.file.StandardOpenOption. There is a fixed set of primitive types: boolean
, byte
, short
, int
, long
, char
, float
, and double
.
For every type of object, the Java virtual machine instantiates an immutable instance of java.lang.Class which provides methods to examine the runtime properties of the object including its members and type information. The class java.lang.Class also provides the ability to create new classes and objects. Most importantly, it is the entry point for all the Reflection APIs. This section explains how you can retrieve the instance of java.lang.Class that you need.
Getting the Class of an Object
If an instance of an object is available, then the simplest way to get its Class is to invoke Object.getClass(). Of course, this only works for reference types which all inherit from Object. Some examples follow.
Getting a Simple Class
The following invokes the getClass() method on an instance of the String class. It returns the Class object for the String class.
Class<?> c = "foo".getClass();
System.out.println("class: " + c);
Running the previous code displays the following:
class: class java.lang.String
Note that the toString()
method of the class Class
adds class
in front of the name of the class.
There is a unique console associated with the virtual machine which is returned by the static method System.console(). The value returned by getClass() is the Class corresponding to java.io.Console.
Class<?> c = System.console().getClass();
System.out.println("class: " + c);
Running the previous code displays the following. Note that java.io.Console is a sealed class, with only one permitted extension: ProxyingConsole
. This class is not public (you can find it with your IDE for instance), so you cannot use it in your code. It is provided to you at runtime by the JDK.
class: c: class java.io.ProxyingConsole
Getting an Enum Class
SATURDAY
is an instance of the enum Days
; thus, in the following example getClass() returns the Class corresponding to the enumeration type Days
.
enum Days {SATURDAY, SUNDAY}
Class<?> c = Days.SATURDAY.getClass();
System.out.println("class: " + c);
Note that what the previous code displays depends on how you declare your enumeration: the package you put it in, and if you declare this enumeration in its own class, or as a local or inner class.
class: class org.devjava.Days
Getting an Array Class
Since arrays are Objects, it is also possible to invoke getClass() on an instance of an array. The returned Class corresponds to an array with component type byte
.
byte[] bytes = new byte[1024];
Class<?> c = bytes.getClass();
System.out.println("class: " + c);
Running the previous example displays the following.
class: class [B
The string [B
corresponds to an array of bytes. It uses a special syntax that we will cover later in this section.
Getting a Runtime Class
In the following case, java.util.Set is an interface to an object of type java.util.HashSet. The value returned by getClass() is the class corresponding to java.util.HashSet.
import java.util.HashSet;
import java.util.Set;
Set<String> s = new HashSet<String>();
Class<?> c = s.getClass();
System.out.println("class: "+c);
Running the previous example displays the following.
class: class java.util.HashSet
Note that the generic type of this set is not there, due to type erasure. You will a way to get the generic type of elements see later in this chapter.
Getting a Lambda Class
Lambda expressions also have classes that you can get, even if it is probably not advisable to do so.
Supplier<String> supplier = () -> "Hello";
Class<?> c = supplier.getClass();
System.out.println("class: " + c);
Running the previous example displays the following. Running it on your machine may produce a different result.
class: class org.devjava.Main$$Lambda/0x00000290d4003538
Getting an Anonymous Class
Anonymous classes are classes created for you by the compiler.
Object key = new Object() {};
Class<?> c = key.getClass();
System.out.println("class: " + c);
Running the previous example displays the following. Running it on your machine may produce a different result.
class: class org.devjava.Main$1
The .class Syntax
If the type is available but there is no instance then it is possible to obtain a Class by appending .class
to the name of the type. This is also the easiest way to obtain the Class for a primitive type.
Calling getClass()
on a primitive type gives you a compile time error, as you can see on the following example.
boolean b = true;
Class<?> c = b.getClass(); // compile-time error
Note that the statement boolean.getClass()
would produce a compile-time error because a boolean
is a primitive type and cannot be dereferenced. The .class
syntax returns the Class corresponding to the type boolean
.
boolean b = true;
Class<?> c = boolean.class; // correct
System.out.println("class: " + c);
Running the previous example displays the following.
class: boolean
You can also use this syntax on reference types. In the following example, the variable c
is the Class corresponding to the type java.io.PrintStream.
Class<?> c = java.io.PrintStream.class;
System.out.println("class: " + c);
Running the previous example displays the following.
class: class java.io.PrintStream
The .class
syntax may be used to retrieve a Class corresponding to a multidimensional array of a given type.
Class<?> c = int[][][].class;
System.out.println("class: " + c);
Running the previous example displays the following.
class: class: class [[[I
This special syntax is explained later in this section.
The Method Class.forName()
If the fully-qualified name of a class is available, it is possible to get the corresponding Class using the static method Class.forName(). This cannot be used for primitive types, but can be used for any array, including arrays of primitive types. The syntax for names of array classes is described by Class.getName(). This syntax is applicable to references and primitive types.
Class<?> c = Class.forName("java.lang.String");
This statement returns the class object from the given fully-qualified name. If this class does not exist, this method throws a ClassNotFoundException.
Here are two examples for a simple array and a bidimensional array.
Class<?> simpleDoubleArray = Class.forName("[D");
Class<?> bidiStringArray = Class.forName("[[Ljava.lang.String;");
The variable simpleDoubleArray
will contain the Class corresponding to an array of primitive type double
(that is, the same as double[].class
). The bidiStringArray
variable will contain the Class corresponding to a two-dimensional array of String
(that is, identical
to String[][].class
).
The Method Class.forPrimitiveName()
A method Class.forPrimitiveName()
was added to the class Class in Java SE 22. This method returns the corresponding class for any primitive type, including void
.
Here is an example of this method in action.
Class<?> c = Class.forPrimitiveName("int");
System.out.println("class: " + c);
Running the previous example displays the following.
class: int
This method returns null if you pass a string that is not a primitive type, and throws a NullPointerException
is you pass null.
Retrieving a Primitive Type From a Wrapper Type
Each of the primitive types and void
has a wrapper class in java.lang
that is used for the boxing of primitive types to reference types.
For instance, the class java.lang.Double
wraps the primitive type double
whenever an Object
is required.
Each wrapper class contains a static field named TYPE
which is equal to the Class for the primitive type being wrapped. This field is public and static, so you can use it wherever you need it.
Here is an example on how you can use this TYPE
field.
Class<?> c = Double.TYPE;
System.out.println("class: " + c);
Running the previous code displays the following.
class: double
You can also use it with the Void
type.
Class c = Void.TYPE;
Void.TYPE
is identical to void.class
.
Getting a Class From Another Class
There are several Reflection APIs which return classes but these may only be accessed if a Class has already been obtained either directly or indirectly.
Getting the Super Class of a Class
The Class.getSuperclass()
method returns the only super class of a given class. It returns null for the Object
class.
The following code prints the super classes of the NullPointerException
class.
Class<?> c = NullPointerException.class;
while (c != null) {
System.out.println("class: " + c);
c = c.getSuperclass();
}
Running the previous example prints the following.
class: class java.lang.NullPointerException
class: class java.lang.RuntimeException
class: class java.lang.Exception
class: class java.lang.Throwable
class: class java.lang.Object
Getting the Implemented Interfaces of a Class
The Class.getInterfaces()
method returns an array of the interfaces directly implemented by a given class. It does not return any interface implemented by the super types of the given class. If the given class is actually an interface, then it returns the interfaces directly extended by the given interface.
You can test this method with the following code.
Class<?> c = String.class;
Class<?>[] implementedInterfaces = c.getInterfaces();
for (Class<?> implementedInterface : implementedInterfaces) {
System.out.println("i: " + implementedInterface);
}
Running the previous example gives you the following result.
i: interface java.io.Serializable
i: interface java.lang.Comparable
i: interface java.lang.CharSequence
i: interface java.lang.constant.Constable
i: interface java.lang.constant.ConstantDesc
You can also call this method on an array type, as in the following example.
int[] ints = {0, 1, 2};
Class<?> superClass = ints.getClass().getSuperclass();
System.out.println("Super class: " + superClass);
Class<?>[] interfaces = ints.getClass().getInterfaces();
System.out.println("Implemented interfaces");
Arrays.stream(interfaces).forEach(System.out::println);
The result is the following.
Super class = class java.lang.Object
Implemented interfaces
interface java.lang.Cloneable
interface java.io.Serializable
It shows two things.
- At runtime, arrays are object, and as such, their type extends
Object
. - this type also implements
Cloneable
andSerializable
.
Getting the Public Member Classes of a Class
The Class.getClasses()
method returns all the public classes, interfaces, records, and enums that are members of the class, and of all its super classes.
The following example shows this method in action on the Character
class.
Class<?> c = Character.class;
for (var memberClass: c.getClasses()) {
System.out.println("class: " + memberClass);
}
Running the previous example prints the following.
class: class java.lang.Character$UnicodeBlock
class: class java.lang.Character$UnicodeScript
class: class java.lang.Character$Subset
Getting all the Member Classes of a Class
The Class.getDeclaredClasses()
method returns an array all the member classes of this class, whether they are public, protected, package protected, or private. Inherited member classes are not returned.
You can try this method on the same Character
class as previously, with the following code.
Class<?> c = Character.class;
for (var memberClass: c.getDeclaredClasses()) {
System.out.println("class: " + memberClass);
}
And you can see that the result is not the same. The class java.lang.Character$CharacterCache
is in the array. It is a private member class of the Character
class. It was not returned previously, because the Class.getDeclaredClasses()
method only returns public members.
class: class java.lang.Character$CharacterCache
class: class java.lang.Character$UnicodeBlock
class: class java.lang.Character$UnicodeScript
class: class java.lang.Character$Subset
Getting the Declaring Class of a Class
The Class.getDeclaringClass()
returns the class in which this class is declared. If this class is not a member class of any class, then this method returns null.
The following example shows this method in action on the Character.UnicodeBlock
class.
Class<?> c = Character.UnicodeBlock.class;
Class<?> declaringClass = c.getDeclaringClass();
System.out.println("class: " + declaringClass);
Running the previous example prints the following. Indeed, the Character.UnicodeBlock
class is a member of the class Character
.
class: class java.lang.Character
Note that the declaring class of an anonymous class is null, as you can see it on the following example.
var key = new Object() {};
Class<?> anonymousClass = key.getClass();
Class<?> declaringClass = anonymousClass.getDeclaringClass();
System.out.println("class: " + declaringClass);
Running this code gives you the following result.
class: null
Getting the Enclosing Class of a Class
The Class.getEnclosingClass()
method returns the immediately enclosing class of a given class. It returns null for a class that is not a member of any class.
There are cases where the enclosing class and the declaring class are the same, as it is the case on the following example.
Class<?> c = Character.UnicodeBlock.class;
Class<?> enclosingClass = c.getEnclosingClass();
System.out.println("class: " + enclosingClass);
Running the previous example prints the following. For this class, calling Class.getDeclaringClass()
and Class.getEnclosingClass()
returns the same class.
class: class java.lang.Character
But in the case of an anonymous class, the result is not the same. The enclosing class is the class in which the anonymous class is declared. You can see it on the following example.
var key = new Object() {};
Class<?> anonymousClass = key.getClass();
Class<?> declaringClass = anonymousClass.getDeclaringClass();
System.out.println("class: " + declaringClass);
The previous code returns the following. Note that you may have a different result.
class: class Main
Getting the Declaring Class of a Class Member
Fields, methods, and constructors are modeled by their own class in the Reflection API: Field
, Method
, and Constructor
. They all implement a common interface: Member
. This interface has several methods, among them is the Member.getDeclaringClass()
method, that returns the class in which the corresponding member is declared.
Fields, methods, and constructors are covered in detail later in this chapter, as well as the other methods of the Member
interface.
Last update: July 19, 2024