https://blog.frankel.ch/hacking-third-party-api-jvm/

The JVM ecosystem is mature and offers plenty of libraries, so you don’t need to reinvent the wheel. Basic - and not so basic - functionalities are just a dependency away. Sometimes, however, the dependency and your use-case are slightly misaligned.

The correct way to fix this would be to create a Pull Request. But your deadline is tomorrow: you need to make it work now! It’s time to hack the provided API.

In this post, we are going through some alternatives that allow you to make third-party APIs behave in a way that their designers didn’t intend to.

Reflection

Imagine that the API has been designed to follow the open-closed principle:

In object-oriented programming, the open–closed principle states "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification"; that is, such an entity can allow its behaviour to be extended without modifying its source code.

Imagine that the dependency’s public API does not fit your use case. You need to extend it, but that’s not possible because the design disallows it - on purpose.

To cope with that, the oldest trick on the JVM in the book is probably reflection.

Reflection is a feature in the Java programming language. It allows an executing Java program to examine or "introspect" upon itself, and manipulate internal properties of the program. For example, it’s possible for a Java class to obtain the names of all its members and display them.

In our scope, reflection allows you to access state that was not meant to be accessed, or call methods that were not meant to be called.

public class Private {

  private String attribute = "My private attribute";

  private String getAttribute() {
    return attribute;
  }
}

public class ReflectionTest {

  private Private priv;

  @BeforeEach
  protected void setUp() {
    priv = new Private();
  }

  @Test
  public void should_access_private_members() throws Exception {
    var clazz = priv.getClass();
    var field = clazz.getDeclaredField("attribute");
    var method = clazz.getDeclaredMethod("getAttribute");
    AccessibleObject.setAccessible(new AccessibleObject[]{field, method}, true);
    field.set(priv, "A private attribute whose value has been updated");
    var value = method.invoke(priv);
    assertThat(value).isEqualTo("A private attribute whose value has been updated");
  }
}
private

private

private

private