Categories
java reflection terminology

What is reflection and why is it useful?

2446

What is reflection, and why is it useful?

I’m particularly interested in Java, but I assume the principles are the same in any language.

6

  • 13

    For me it is a way of getting class names at runtime and creating objects of that class.

    – Nabin

    Nov 3, 2016 at 5:02

  • 92

    because this is a popular question I’d like to point out that reflection (Without annotations) should be the very last tool you go to when solving a problem. I use it and love it, but it cancels out all the advantages of Java’s static typing. If you do need it, isolate it to as small an area as possible (One method or one class). It’s more acceptable to use it in tests than production code. With annotations it should be fine–The main point is not to specify class or method names as “Strings” if you can possibly avoid it.

    – Bill K

    Apr 4, 2017 at 21:45


  • 2

  • 8

    In addition to @BillK’s comment: Reflection is very powerful, I’d call it magic. With great power comes great responsibility. Use it only if you know what you’re doing.

    Nov 22, 2018 at 13:02

  • 1

    @Trap I don’t know, that’s why I was recommending against it—it’s really annoying when I run into reflection when there were other solutions available, or reflection that isn’t isolated to a very small, constrained and clearly documented area of the code. But asking why programmers do what they do is beyond my ability to answer.

    – Bill K

    Jun 12, 2020 at 21:03

1951

The name reflection is used to describe code which is able to inspect other code in the same system (or itself).

For example, say you have an object of an unknown type in Java, and you would like to call a ‘doSomething’ method on it if one exists. Java’s static typing system isn’t really designed to support this unless the object conforms to a known interface, but using reflection, your code can look at the object and find out if it has a method called ‘doSomething’ and then call it if you want to.

So, to give you a code example of this in Java (imagine the object in question is foo) :

Method method = foo.getClass().getMethod("doSomething", null);
method.invoke(foo, null);

One very common use case in Java is the usage with annotations. JUnit 4, for example, will use reflection to look through your classes for methods tagged with the @Test annotation, and will then call them when running the unit test.

There are some good reflection examples to get you started at http://docs.oracle.com/javase/tutorial/reflect/index.html

And finally, yes, the concepts are pretty much similar in other statically typed languages which support reflection (like C#). In dynamically typed languages, the use case described above is less necessary (since the compiler will allow any method to be called on any object, failing at runtime if it does not exist), but the second case of looking for methods which are marked or work in a certain way is still common.

Update from a comment:

The ability to inspect the code in the system and see object types is
not reflection, but rather Type Introspection. Reflection is then the
ability to make modifications at runtime by making use of
introspection. The distinction is necessary here as some languages
support introspection, but do not support reflection. One such example
is C++

13

  • 42

    can u please explain what is the significance of that null parameter in this line Method method = foo.getClass().getMethod(“doSomething”, null);

    Nov 23, 2012 at 5:59

  • 63

    The null indicates there are no parameters being passed to the foo method. See docs.oracle.com/javase/6/docs/api/java/lang/reflect/…, java.lang.Object…) for details.

    Nov 27, 2012 at 1:14

  • 868

    Just to clear up since this has so many upvotes. The ability to inspect the code in the system and see object types is not reflection, but rather Type Introspection. Reflection is then the ability to make modifications at runtime by making use of introspection. The distinction is necessary here as some languages support introspection, but do not support reflection. One such example is C++.

    Mar 12, 2013 at 3:46

  • 51

    I love reflection but if you have control over the code then using reflection as specified in this answer is unnessary and therefore an abuse–You should use Type Introspection (instanceof) and strong types. If there is any way but reflection to do something, that’s how it should be done. Reflection causes serious heartache because you lose all advantages of using a statically typed language. If you need it you need it, however even then I’d consider a pre-packaged solution like Spring or something that completely encapsulates the reflection necessary–IE: let someone else have the headaches.

    – Bill K

    Jul 29, 2013 at 15:50


  • 6

    @bigtunacan Where did you get that information from? I see the term “reflection” used in official Java documentation from Oracle to describe not only the ability to make changes at runtime but also the ability to see the type of an object. Not to mention that most so-called “type introspection” related classes (ex: Method, Constructor, Modifier, Field, Member, basically apparently all except Class) are within the java.lang.*reflect* package. Perhaps the concept “reflection” comprehensively includes both “type introspection” and modification at run-time?

    Apr 29, 2016 at 9:34


289

Reflection is a language’s ability to inspect and dynamically call classes, methods, attributes, etc. at runtime.

For example, all objects in Java have the method getClass(), which lets you determine the object’s class even if you don’t know it at compile time (e.g. if you declared it as an Object) – this might seem trivial, but such reflection is not possible in less dynamic languages such as C++. More advanced uses lets you list and call methods, constructors, etc.

Reflection is important since it lets you write programs that do not have to “know” everything at compile time, making them more dynamic, since they can be tied together at runtime. The code can be written against known interfaces, but the actual classes to be used can be instantiated using reflection from configuration files.

Lots of modern frameworks use reflection extensively for this very reason. Most other modern languages use reflection as well, and in scripting languages (such as Python) they are even more tightly integrated, since it feels more natural within the general programming model of those languages.

3

  • 3

    So in other words, you can create an instance out of it’s qualified name and the compiler won’t complain about it (because say you use just a String for the class name). Then, at run time, if that class is not present you get an exception. You kind of bypassed the compiler in this case. Would you give me some specific use case for this? I just can’t picture when i would choose it.

    Dec 1, 2018 at 21:40

  • 4

    @FernandoGabrieli while it is true that it is easy to create runtime errors with reflection, it is also perfectly possible to use reflection without risking runtime exceptions. As hinted in my answer, a common use of reflection is for libraries or frameworks, which explicitly can’t know the structure of the application at compile time, since they are compiled separate from the application. Any library that uses “code by convention” is likely to use reflection, but not necessarily using magic strings.

    – Liedman

    Jan 15, 2019 at 12:25

  • 1

    C++ does have Run-time type information. RTTI

    Dec 10, 2019 at 20:32

125

One of my favorite uses of reflection is the below Java dump method. It takes any object as a parameter and uses the Java reflection API to print out every field name and value.

import java.lang.reflect.Array;
import java.lang.reflect.Field;

public static String dump(Object o, int callCount) {
    callCount++;
    StringBuffer tabs = new StringBuffer();
    for (int k = 0; k < callCount; k++) {
        tabs.append("\t");
    }
    StringBuffer buffer = new StringBuffer();
    Class oClass = o.getClass();
    if (oClass.isArray()) {
        buffer.append("\n");
        buffer.append(tabs.toString());
        buffer.append("[");
        for (int i = 0; i < Array.getLength(o); i++) {
            if (i < 0)
                buffer.append(",");
            Object value = Array.get(o, i);
            if (value.getClass().isPrimitive() ||
                    value.getClass() == java.lang.Long.class ||
                    value.getClass() == java.lang.String.class ||
                    value.getClass() == java.lang.Integer.class ||
                    value.getClass() == java.lang.Boolean.class
                    ) {
                buffer.append(value);
            } else {
                buffer.append(dump(value, callCount));
            }
        }
        buffer.append(tabs.toString());
        buffer.append("]\n");
    } else {
        buffer.append("\n");
        buffer.append(tabs.toString());
        buffer.append("{\n");
        while (oClass != null) {
            Field[] fields = oClass.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                buffer.append(tabs.toString());
                fields[i].setAccessible(true);
                buffer.append(fields[i].getName());
                buffer.append("=");
                try {
                    Object value = fields[i].get(o);
                    if (value != null) {
                        if (value.getClass().isPrimitive() ||
                                value.getClass() == java.lang.Long.class ||
                                value.getClass() == java.lang.String.class ||
                                value.getClass() == java.lang.Integer.class ||
                                value.getClass() == java.lang.Boolean.class
                                ) {
                            buffer.append(value);
                        } else {
                            buffer.append(dump(value, callCount));
                        }
                    }
                } catch (IllegalAccessException e) {
                    buffer.append(e.getMessage());
                }
                buffer.append("\n");
            }
            oClass = oClass.getSuperclass();
        }
        buffer.append(tabs.toString());
        buffer.append("}\n");
    }
    return buffer.toString();
}

7

  • 8

    What should Callcount be set to?

    – Tom

    Apr 29, 2010 at 13:06

  • 9

    I got a Exception in thread “AWT-EventQueue-0” java.lang.StackOverflowError when I ran this.

    – Tom

    Apr 29, 2010 at 13:13

  • 3

    @Tom callCount should be set to zero. It’s value is used to determine how many tabs should precede each line of output: each time dump needs to dump a “subobject”, the output would print as nested in the parent. That method proves useful when wrapped in another. Consider printDump(Object obj){ System.out.println(dump(obj, 0)); }.

    – fny

    Nov 23, 2012 at 6:09


  • 1

    The java.lang.StackOverflowError might be created in case of circular references, because of the unchecked recursion: buffer.append(dump(value, callCount))

    – Arnaud P

    Jul 1, 2013 at 23:03


  • 4

    Can you specifically release your code to Public Domain, pretty please?

    – stolsvik

    Oct 8, 2013 at 10:06