15/07/2023
7 min read
Java

Content:

Reflection in Java by example






Introduction


Reflection is the ability of a process to examine, introspect, and modify its own structure (from Wikipedia). Reflection is not a concepet exclusive to Java in fact it is part of many other programming language such as Javascript, PHP, C# etc…

In this article we will see why it is used , how to use it (through a little kata) and we will also talk about the advantages and drawbacks of this powerful concept.


Reflection Set Kata


This Kata is inspired by an example of the item 65 of the great book Effective Java by Joshua Bloch.


Description:


Build a class that dynamically instantiate an implementation of a Set of string (HashSet, TreeSet etc…) and prints its content.

You must handle the following errors by logging those sentences :

ClassNotFoundException: This class does not exist

NoSuchMethodException: This class has no parameterless constructor

ArrayIndexOutOfBoundsException: No implementation of Set chosen

All other exceptions: this class does not implement Set


Solving the Kata with Reflection:



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package reflectionSetKata; import java.util.Arrays; import java.util.Map; import java.util.Set; public class PrintDynamicSetImplContent { private final Logger logger; private final Map<Class<? extends Exception>,String> mapErrorToMessage = Map.of( ClassNotFoundException.class,"This class does not exist", NoSuchMethodException.class,"This class has no parameterless constructor", ArrayIndexOutOfBoundsException.class,"No implementation of Set chosen" ); public PrintDynamicSetImplContent(Logger logger) { this.logger = logger; } public void execute(String[] args) { try { @SuppressWarnings("unchecked") Set<String> set = (Set<String>) Class .forName(args[0]) .getDeclaredConstructor() .newInstance(); set.addAll(Arrays.asList(args).subList(1, args.length)); logger.log(set); } catch (Exception e) { logger.log(mapErrorToMessage.getOrDefault(e.getClass(),"This class does not implement Set")); } } }


The className of the Set implementation is passed as the first argument in the array of string (args) and we can dynamically create the class (thanks to the reflection api) with Class.forName. This action throws a ClassNotFoundException if the class does not exist and of course if the array is empty it will throws an ArrayIndexOutOfBoundsException .

Then we get the parameterless constructor of the class again this action throws NoSuchMethodException if the class has no parameterless constructor (example an Interface).

And finally we instantiate the class if this action didn't fail we have our empty Set and we don't known if it is a HashSet or LinkedHashSet etc… the implementation is chosen at runtime.

We get the rest of the array, put it in the set and log it.

We just build quite flexible program that enables us to switch of Set implementation without touching the code.

If i pass this array to my program new String[]{"java.util.TreeSet","c","b","a"} it will prints ["a","b","c"] because a TreeSet sorts its content but if i pass new String[]{"java.util.LinkedHashSet","c","b","a"} it will prints ["c","b","a"] because a LinkedHashSet keep its content ordered by the order of its items insertion.

Advantages and Drawbacks of Reflection:


Advantages :


Reflection unables us to write generic programs that can be reused in a lot of situation in fact a lot of famous framworks that are used for dependency injection (Spring) , testing (Mockito) , logging (Log4J) and a lot of others use reflection.


Drawbacks:


You lose the benefits of compile-time type checking:

If your program try to invokes reflectivly a method that does not exists it will only fail at run times, In fact you can even see in our little kata that we have to do an unchecked cast to Set&lt;string>.


Reflection is verbose, not easy to read and hard to debug:


As you can see in the little program that we wrote a lot of things can go wrong when you use reflection so you have a lot of error cases to handle and that can hurts the readabilty of your code.


Relection is Slow:


Reflective method is much more slower than normal method invocation. it is one of the reason of why i write my own test doubles in Java and don't generally use any mocking frameworks.

The other reasons are well sumarized in this great article from Uncle Bob.



Bonus DI with relfection:


Let's build a Main class to run the program that we just build. If you paid attention you saw that a logger is injected in the class PrintDynamicSetImplContent, the Logger correspond to this interface:



1 2 3 public interface Logger { void log(Object anyObject); }


We will instantiate dynamically with reflection the implmentation of the Logger interface and pass it to the PrintDynamicSetImplContent.

We will get the name from the logger implementation class from a config.text file with the help of the java.util.Scanner class and java.io.File.


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package reflectionSetKata; import java.io.File; import java.util.Scanner; public class Main { public static void main(String[] args) { try (Scanner scanner = new Scanner(new File("./src/reflectionSetKata/config.text"))) { String loggerImplClassName = scanner.next(); Logger logger = (Logger) Class.forName(loggerImplClassName) .getDeclaredConstructor() .newInstance(); PrintDynamicSetImplContent printDynamicSetImplContent = new PrintDynamicSetImplContent(logger); printDynamicSetImplContent.execute(args); } catch (Exception e) { e.printStackTrace(); } } }



And voila, now we can change of Logger implementation without touching the code just by changing the config.text file.

Now you have a rough idea of how IOC container works (of course is way more elaborate than this naive example).


Conclusion:


In this article we explored with a concrete example what is reflection, what are the different use cases for using it, its advantages as well as its drawbacks.

Maybe like me, you conclude that if you don't write a generic framework the tradeoff that you have to make by using reflection almost never worth it.

You can find all the coding examples used in this article here.