# Using reflection in Java

**URL:** https://bnd.discourse.group/t/using-reflection-in-java/414
**Category:** TIPS
**Created:** [January 30, 2024, 1:41pm UTC](https://bnd.discourse.group/t/using-reflection-in-java/414 "2024-01-30T13:41:50Z")
**Posts on this page:** 1
**Page:** 1

<div class="post-metadata">

### Author: ![pkriens](https://yyz2.discourse-cdn.com/free1/user_avatar/bnd.discourse.group/pkriens/32/3_2.png) [@pkriens](https://bnd.discourse.group/u/pkriens)
#### Post date: [January 30, 2024, 1:41pm UTC](https://bnd.discourse.group/t/using-reflection-in-java/414/1 "2024-01-30T13:41:50Z")

</div>

Maybe not so much a bnd tip … However, I am a great fan of Java reflection and much of the code I write uses reflection to make my code more _declarative_. Yesterday I had a great example and just want to share it.

Domain Specific Languages (DSL) are a well known technique to _reduce boiler plate_ code. Where boiler plate code is _very low density code_ that requires not a lot of brainpower to write but feels offensive that one has to write it. True, AI can nowadays do a lot of it but then we’re still left to have to read it now and then. Although it is low density code, the quantity then tends to obscure potential errors. And changing this kind of code is, at least for me, very error prone. So I am very much in favor of DSLs.

Creating a DSL parser is unfortunately a lot of work. Before you have the editor, the completion support, the quick fixes, the parser, and the runtime you’ve spent quite a lot of hours. Sometimes the DSL requires so many special tricks that this is worth it. Most of the times it isn’t, so we wrestle through the mud of boiler plate code.

One technique I use a lot is to create an _interface_ that models a DSL. Since interfaces are already first class Java objects, the IDE tends to already have a lot of help built in. One of my most favorite methods in Java is therefore `Proxy.newProxyInstance()`. It creates a proxy object that implements all the given interfaces. It is highly popular among general framework developers like Spring but I have observed that it tends to be overlooked by many application developers. (Or tell me if I am wrong?)

Yesterday I was working on an API for the [Assistant OpenAI API](https://platform.openai.com/docs/api-reference/assistants). Since I am also a relatively late convert to `final` fields and immutable objects, I noticed myself writing a lot of _builders_. Where a builder is a class that gathers the information to create an immutable object. The builder pattern is where each method returns the builder itself so you can configure an object in a few short lines.

```java
	interface AssistantBuilder {
		AssistantBuilder metadata(String key, String value);
		AssistantBuilder name(String name);
		AssistantBuilder description(String description);
		AssistantBuilder instructions(String instructions);
		Assistant get();
	}

```

Such an interface can then be used to construct an Assistant:

```java
OpenAI provider = ...;
Assistant assistant = provider.assistant()
   .name("myassistant")
   .description("""
        A bndtools magic helper to reduce coding time, 
        review code, and assist you in finding the meaning 
        of life
   """)
   .instructions("""
         ....
   """)
   .metadata("aQute.search", "test.1")
   .get();

```

Implementing 10 of such builders is just no fun, even using Chat GPT. And maintaining low density code was not what I was built for. So while writing the second builder my mind started to wonder and soon I saw my fingers type `Proxy.newProxyInstance(` …

The basic idea of the Proxy is that you provide the interfaces the returned proxy object should implement and then an Invocation Handler, which is _functional interface_ with one method:

```java
   Object invoke(Object proxy, Method method, Object ... args) throws Throwable

```

When a method on the proxy gets called, it is delegated to the Invocation Handler. The Invocation Handler gets the Method object that was called and the actual arguments. In this case we need to gather the information so that in the `get()` method we have all the possible information in place for a construction method.  
In the case of the Assistant the builder’s actual data was quite simple. Either a scalar like String or a Number and the _metadata_ parameters map. For the scalars, model them as a single argument method and put the argument in a map under the name of the method. For the metadata it is slightly harder. The object is a Map itself. The metadata takes two String arguments. In that case, ensure the parameters map has a Map object under the name of the method, and put the second argument with the method name as key.  
To finally create the object that is being build, we use a factory`Function<Map<String,Object>,T>`. The caller can then use it for any type and factory method it likes.  
So without further ado, the code:

```java
 static <B, T> B builder(Class<B> builderInterface, Function<Map<String, Object>, T> build) {
	Map<String, Object> parameters = new LinkedHashMap<>();
	return (B) Proxy.newProxyInstance(
            builderInterface.getClassLoader(), 
            new Class[] { builderInterface },
			(p, m, args) -> {
				if (m.getName().equals("get") 
                && m.getParameterCount() == 0) {
				return build.apply(parameters);
			}
			if (m.getParameterCount() == 2) {
				Map<String, Object> map = 
                  (Map<String, Object>) parameters.computeIfAbsent(m.getName(),
					k -> new LinkedHashMap<>());
				map.put((String) args[0], args[1]);
			} else if (m.getParameterCount() == 1) {
				parameters.put(m.getName(), args[0]);
			} else
				throw new IllegalArgumentException(
						"""
                           A builder interface must have a get() method, 2 arg 
                           methods for map parameters, and 1 arg methods for parameters
                        """);
				return p;
			});
}

```

In an OSGi world it is quite important to know where the classes are loaded from. In this case, we load the classes from the same class loader as the interface. This is usually the best solution in OSGi as far as I know.
