The TypeSafe classes, and their descendants, need a mechanism to find out what type has been used as a parameter for the concrete matcher. Unfortunately, this type is lost during type erasure so we need to use reflection to get it back, by picking out the type of a known parameter to a known method. The catch is that, with bridging methods, this type is only visible in the class that actually implements the expected method, so the ReflectiveTypeFinder needs to be applied to that class or a subtype. For example, the abstract TypeSafeDiagnosingMatcher<T> defines an abstract method
protected abstract boolean matchesSafely(T item, Description mismatchDescription);
By default it uses new ReflectiveTypeFinder("matchesSafely", 2, 0); to find the parameterised type. If we create a TypeSafeDiagnosingMatcher<String>, the type finder will return String.class. A FeatureMatcher is an abstract subclass of TypeSafeDiagnosingMatcher. Although it has a templated implementation of matchesSafely(<T>, Decription);, the actualy run-time signature of this is matchesSafely(Object, Description);. Instead, we must find the type by reflecting on the concrete implementation of
protected abstract U featureValueOf(T actual);
a method which is declared in FeatureMatcher. In short, use this to extract a type from a method in the leaf class of a templated class hierarchy.
Author:Steve Freeman, Nat Pryce
/** * The TypeSafe classes, and their descendants, need a mechanism to find out what type has been used as a parameter * for the concrete matcher. Unfortunately, this type is lost during type erasure so we need to use reflection * to get it back, by picking out the type of a known parameter to a known method. * The catch is that, with bridging methods, this type is only visible in the class that actually implements * the expected method, so the ReflectiveTypeFinder needs to be applied to that class or a subtype. * * For example, the abstract <code>TypeSafeDiagnosingMatcher&lt;T&gt;</code> defines an abstract method * <pre>protected abstract boolean matchesSafely(T item, Description mismatchDescription);</pre> * By default it uses <code>new ReflectiveTypeFinder("matchesSafely", 2, 0); </code> to find the * parameterised type. If we create a <code>TypeSafeDiagnosingMatcher&lt;String&gt;</code>, the type * finder will return <code>String.class</code>. * * A <code>FeatureMatcher</code> is an abstract subclass of <code>TypeSafeDiagnosingMatcher</code>. * Although it has a templated implementation of <code>matchesSafely(&lt;T&gt;, Decription);</code>, the * actualy run-time signature of this is <code>matchesSafely(Object, Description);</code>. Instead, * we must find the type by reflecting on the concrete implementation of * <pre>protected abstract U featureValueOf(T actual);</pre> * a method which is declared in <code>FeatureMatcher</code>. * * In short, use this to extract a type from a method in the leaf class of a templated class hierarchy. * * @author Steve Freeman * @author Nat Pryce */
package org.hamcrest.internal; import java.lang.reflect.Method; public class ReflectiveTypeFinder { private final String methodName; private final int expectedNumberOfParameters; private final int typedParameter; public ReflectiveTypeFinder(String methodName, int expectedNumberOfParameters, int typedParameter) { this.methodName = methodName; this.expectedNumberOfParameters = expectedNumberOfParameters; this.typedParameter = typedParameter; } public Class<?> findExpectedType(Class<?> fromClass) { for (Class<?> c = fromClass; c != Object.class; c = c.getSuperclass()) { for (Method method : c.getDeclaredMethods()) { if (canObtainExpectedTypeFrom(method)) { return expectedTypeFrom(method); } } } throw new Error("Cannot determine correct type for " + methodName + "() method."); }
Params:
  • method – The method to examine.
Returns:true if this method references the relevant type
/** * @param method The method to examine. * @return true if this method references the relevant type */
protected boolean canObtainExpectedTypeFrom(Method method) { return method.getName().equals(methodName) && method.getParameterTypes().length == expectedNumberOfParameters && !method.isSynthetic(); }
Params:
  • method – The method from which to extract
Returns:The type we're looking for
/** * @param method The method from which to extract * @return The type we're looking for */
protected Class<?> expectedTypeFrom(Method method) { return method.getParameterTypes()[typedParameter]; } }