Reflection

Reflection in Java enables programs to inspect the structure of unknown classes at runtime, create instances of classes, access their fields, and run methods. The root class Object defines a method getClass which return an object of class Class which allows information about the class of an object to be retrieved, e.g., get all fields of the class. This allows not just for passive inspection of the class structure, but also enables objects of a class to be created dynamically, to call methods of an object of the class, or to change or retrieve the value of an object's fields. Furthermore, every class has a static field class which allows the Class object corresponding to this class to be accessed. Have a look at the documention of this classes methods to learn more:

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Class.html

In [1]:
import java.util.Arrays;

Class strClass = String.class;

return Arrays.toString(strClass.getFields());
Out[1]:
[public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER]
In [2]:
package lecture;

public class A {
    public int x;
    public String y;
    
    public void setBoth(int x, String y) {
        this.x = x;
        this.y = y;
    }
    
    public String toString() {
        return "x: " + x + ", y: " + y;
    }
    
}
Out[2]:
lecture.A
In [3]:
import lecture.A;
import java.util.Arrays;

A a = new A();
Class aClass = a.getClass();
return Arrays.toString(aClass.getFields());
Out[3]:
[public int lecture.A.x, public java.lang.String lecture.A.y]
In [6]:
import lecture.A;
import java.util.Arrays;

A a = new A();
Class aClass = a.getClass();
return Arrays.toString(aClass.getMethods()).replaceAll("\\),", "\\)\n");
Out[6]:
[public void lecture.A.setBoth(int,java.lang.String)
 public java.lang.String lecture.A.toString()
 public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException, public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException, public final void java.lang.Object.wait() throws java.lang.InterruptedException, public boolean java.lang.Object.equals(java.lang.Object)
 public native int java.lang.Object.hashCode()
 public final native java.lang.Class java.lang.Object.getClass()
 public final native void java.lang.Object.notify()
 public final native void java.lang.Object.notifyAll()]
In [7]:
import lecture.A;
import java.util.Arrays;
import java.lang.reflect.Method;

A a = new A();
Class aClass = a.getClass();
Method setBoth = aClass.getMethod("setBoth", int.class, String.class); // get setBoth method
setBoth.invoke(a, 1, "helloh"); // call setBoth(1,"helloh") on a

Method toStr = aClass.getMethod("toString"); // get toString methdo
return (String) toStr.invoke(a); // call a's toString method
Out[7]:
x: 1, y: helloh

Annotations

Annotations allow additional information to be stores with classes, fields, and methods. This information is accessible through reflection. Annotation are declared using <modifiers> @interface <name> {... where <name> is the name of the annotation. Methods in annotations are restricted to methods without parameters that do not throw exceptions. These methods determine the elements that can be stored in an annotation. Annotations are attached to classes, fields and methods using the syntax @A or @A(<key-value pairs>) where A is an annotation type. @A(<key value pairs) assigns values to the elements of the annotation. We show usage of build-in annotations here since beakerx does not support annotation definition. See here for an example.

In [35]:
package lecture;

public class OverridesSomething {
    
    @Override // we use the build-in annotation @Override to indicate that this method is overriding a method declared by a superclass.
    public String toString() {
        return "I've got nothing";
    }
    
}
Out[35]:
lecture.OverridesSomething
In [39]:
import lecture.OverridesSomething;
import java.util.Arrays;
import java.lang.reflect.Method;
import java.lang.annotation.Annotation;

Class c = OverridesSomething.class;
Method m = c.getMethod("toString");
Annotation[] a = m.getAnnotations(); // not all annotations are available at runtime. @Override is not
return Arrays.toString(a);
Out[39]:
[]