Skip to content

thecodewarrior/Mirror

Repository files navigation



Mirror

Unlocking the power of reflection

The central feature of Mirror is the family of “Mirrors,” which wrap Java Core Reflection objects and make many advanced reflection features simple and easy to use. Mirrors can contain information about type arguments and annotations, meaning, among other things, the superclass of ArrayList<@NotNull String> is AbstractList<@NotNull String>, not the raw AbstractList or generic AbstractList<T>.

Major features so far

  • Type specialization
    • Elevates generics and type annotations to be fully fledged members of the type system
  • Near-native speed reflective member access using MethodHandles
  • All-around easier access to information
    • Out with the Modifier.isStatic(someClass.modifiers), in with the classMirror.isStatic()
// create a mirror of LinkedList<String>
ClassMirror linkedList = Mirror.reflectClass(new TypeToken<LinkedList<String>>() {});
// get the `removeFirst` method, which is defined in Deque
MethodMirror removeFirst = linkedList.getMethod("removeFirst");

// the type argument `String` has propagated down and specialized this method's return type
assert removeFirst.getReturnType().equals(Mirror.reflect(String.class));
// this mirror of the method is actually located in the `Deque<String>` class
assert removeFirst.getDeclaringClass().equals(Mirror.reflect(new TypeToken<Deque<String>>(){}));

// after the initial overhead of creating the MethodHandle, this will run at near-native speed
String value = removeFirst.call(linkedListInstance);

Minor features

Generic-aware isAssignableFrom

ClassMirror integerLinkedList = Mirror.reflect(new TypeToken<LinkedList<Integer>>() {});
ClassMirror stringList = Mirror.reflect(new TypeToken<List<String>>() {});
ClassMirror numberList = Mirror.reflect(new TypeToken<List<Number>>() {});

assert !stringList.isAssignableFrom(integerLinkedList);
assert numberList.isAssignableFrom(integerLinkedList);

Type specificity comparison

List<TypeMirror> mirrors = Arrays.asList(
    Mirror.reflect(new TypeToken<List<Number>>() {}),
    Mirror.reflect(new TypeToken<ArrayList<Integer>>() {}),
    Mirror.reflect(new TypeToken<List<Integer>>() {}),
    Mirror.reflect(new TypeToken<List<Object>>() {})
);
mirrors.sort(Comparator.comparing(TypeMirror::getSpecificity));
// mirrors = 
//    List<Object>,
//    List<Number>,
//    List<Integer>,
//    ArrayList<Integer>

Creating annotation instances

Contract contractAnnotation = Mirror.newAnnotation(Contract.class,
    new Pair<>("value", "_, null -> null"),
    new Pair<>("pure", true)
);

// builder style has not been implemented yet. Pairs are convenient in Kotlin but not so much in Java.
Contract contractAnnotation = Mirror.newAnnotation(Contract.class)
    .set("value", "_, null -> null")
    .set("pure", true)
    .build();

Mirror is designed to more broadly follow a source-focused representation of reflection data as opposed to a bytecode-focused representation. This extends to things like the ClassMirror.toString() method returning package.OuterClass.InnerClass instead of package.OuterClass$InnerClass.

… readme wip …