Related
I tried to do:
public class HelloWorld {
public static void main(String... args){
final String string = "a";
final Supplier<?> supplier = string::isEmpty;
System.out.println(supplier);
}
}
I get:
HelloWorld$$Lambda$1/471910020#548c4f57
I would like to get the string isEmpty. How can I do this?
EDIT: the code of the method I created is this one:
public class EnumHelper {
private final static String values = "values";
private final static String errorTpl = "Can't find element with value `{0}` for enum {1} using getter {2}()";
public static <T extends Enum<T>, U> T getFromValue(T enumT, U value, String getter) {
#SuppressWarnings("unchecked")
final T[] elements = (T[]) ReflectionHelper.callMethod(enumT, values);
for (final T enm: elements) {
if (ReflectionHelper.callMethod(enm, getter).equals(value)) {
return enm;
}
}
throw new InvalidParameterException(MessageFormat.format(errorTpl, value, enumT, getter));
}
}
The problem is I can't pass as parameter T::getValue, since getValue is not static. And I can't pass someEnumElem::getValue, since the get() will return the value of that element. I could use inside the for loop:
Supplier<U> getterSupllier = enm:getValue;
if (getterSupllier.get().equals(value)) {
[...]
}
but in this way getValue is fixed, I can't pass it as parameter. I could use some third-party library to do an eval(), but I really don't want to open that Pandora vase :D
EDIT 2: Function does work with no parameters methods, but only in Java 11. Unluckily I'm stuck with Java 8.
string::isEmpty will be constructed by a method LambdaMetafactory.metafactory which has implMethod among its parameters.
final String methodName = implMethod.internalMemberName().getName();
would return a method name (here, "isEmpty") if we had access to the arguments passed to this factory method, and to implMethod in particular. The arguments generated by up-calls from the JVM that provides very specific information for the java.lang.invoke API.
For example, to initialise a DirectMethodHandle which string::isEmpty represents, the JVM will call the following method.
/**
* The JVM is resolving a CONSTANT_MethodHandle CP entry. And it wants our help.
* It will make an up-call to this method. (Do not change the name or signature.)
* The type argument is a Class for field requests and a MethodType for non-fields.
* <p>
* Recent versions of the JVM may also pass a resolved MemberName for the type.
* In that case, the name is ignored and may be null.
*/
static MethodHandle linkMethodHandleConstant(Class<?> callerClass, int refKind,
Class<?> defc, String name, Object type)
That name (exactly what you requested) will be put there by the JVM, and there is no means for us to access it. For now.
To read:
Explicit use of LambdaMetafactory
What are CONSTANT_MethodHandle, CONSTANT_MethodType, and CONSTANT_InvokeDynamic?
MethodHandle - What is it all about?
In short: no.
Once a method reference is used you'll have an implementation of the functional interface that you requested (Supplier<?> in this case), but basically all the specifics of that object as undefined (or implementation-defined to be precise).
The spec doesn't say anything about it being a separate object, what its toString() has to be or what else you can do with it. It's a Supplier<?> and basically nothing else.
The same thing applies to lambda expressions.
So if you had used
final Supplier<?> supplier = () -> string.isEmpty();
the Supplier would do the same thing and you also couldn't get back to the "code" of the lambda.
In short: No, it's not possible.
A workaround that I've been using is to create methods that wrap java.util.functional instances into "named" versions.
import java.util.Objects;
import java.util.function.Supplier;
public class Named {
public static void main(String[] args) {
String string = "a";
Supplier<?> supplier = string::isEmpty;
Supplier<?> named = named("isEmpty", supplier);
System.out.println(named);
}
static <T> Supplier<T> named(String name, Supplier<? extends T> delegate) {
Objects.requireNonNull(delegate, "The delegate may not be null");
return new Supplier<T>() {
#Override
public T get() {
return delegate.get();
}
#Override
public String toString() {
return name;
}
};
}
}
Of course this does not make sense for all application cases. Most importantly, it does not allow you to "derive" things like the method name of a Supplier in hindsight when you just receive it, for example, as a method argument. The reason for that is more technical, most importantly: The supplier does not have to be a method reference.
But when you control the creation of the Supplier, changing string::isEmpty to Named.named("isEmpty", string::isEmpty) can be a reasonable way to go.
In fact, I did this so systematically for all the functional types that I even considered pushing this into some publicly visible (GitHub/Maven) library...
It’s weird that you are asking about the opposite of what you actually need.
You have a method that receives a string and wants to execute a method with that name, but for some unknown reason, you ask for the opposite, to get the method name from an existing supplier.
And already written in a comment before knowing the actual code, you can solve the actual problem by replacing the String getter parameter with Function<T,U> getter.
You don’t need any Reflection tool here:
public class EnumHelper {
private final static String errorTpl
= "Can't find element with value `{0}` for enum {1} using getter {2}()";
public static <T extends Enum<T>, U> T getFromValue(
T enumT, U value, Function<? super T, ?> getter) {
final T[] elements = enumT.getDeclaringClass().getEnumConstants();
for (final T enm: elements) {
if(getter.apply(enm).equals(value)) {
return enm;
}
}
throw new IllegalArgumentException(
MessageFormat.format(errorTpl, value, enumT, getter));
}
}
The getter Function can be implemented via method reference, e.g.
ChronoUnit f = EnumHelper.getFromValue(
ChronoUnit.FOREVER, Duration.ofMinutes(60), ChronoUnit::getDuration);
System.out.println(f);
I made the signature of the function parameter more generous compared to Function<T,U>, to raise the flexibility regarding existing functions, e.g.
Function<Object,Object> func = Object::toString;
ChronoUnit f1 = EnumHelper.getFromValue(ChronoUnit.FOREVER, "Years", func);
System.out.println(f1.name());
If printing meaningful names in the erroneous case is really important, just add a name parameter just for reporting:
public static <T extends Enum<T>, U> T getFromValue(
T enumT, U value, Function<? super T, ?> getter, String getterName) {
final T[] elements = enumT.getDeclaringClass().getEnumConstants();
for (final T enm: elements) {
if(getter.apply(enm).equals(value)) {
return enm;
}
}
throw new IllegalArgumentException(
MessageFormat.format(errorTpl, value, enumT, getterName));
}
to be called like
ChronoUnit f = EnumHelper.getFromValue(
ChronoUnit.FOREVER, Duration.ofMinutes(60), ChronoUnit::getDuration, "getDuration");
That’s still better than using Reflection for the normal operations…
I am hoping to reach the Java generics experts here. Let's say you have some typed class:
public interface SomeClass<T> {
void doSomething(final T t);
}
There is also a function which gets you an instance of T given an instance of SomeClass<T>:
public static class Retriever {
public <T> T get(final SomeClass<T> c) {
return null; // actual implementation left out
}
}
Now let's say you have a collection of SomeClass<?> and a retriever:
final List<SomeClass<?>> myClasses = null; // actual implementation left out
final Retriever myRetriever = null; // actual implementation left out
We are not able to do the following:
for (final SomeClass<?> myClass : myClasses) {
myClass.doSomething(myRetriever.get(myClass));
}
Now my question: does Java need support to be able to locally define a type? Something like:
<T> for (final SomeClass<T> myClass : myClasses) {
myClass.doSomething(myRetriever.get(myClass));
}
Here, the type T is scoped to the for-loop. We are defining T to get rid of the wildcard ?. That's it. The introduction of T should enable us to write the desired for loop as expressed above.
FWIW, the following code is a workaround. We are introducing a function, solely for the conversion of ? to T.
for (final SomeClass<?> myClass : myClasses) {
workAround(myRetriever, myClass);
}
public static <T> void workAround(final Retriever myRetriever, final SomeClass<T> myClass) {
myClass.doSomething(myRetriever.get(myClass));
}
A locally defined user type might be a more elegant solution?
Now my question: does Java need support to be able to locally define a type?
No. The minimal scope of a type-parameter is the method, i.e. in order to have the type T available for your for loop, you will have to either defined the enclosing method a generic or the enclosing class. For example:
<T> void method(List<SomeClass<T> myClasses) {
for (final SomeClass<T> myClass : myClasses) {
myClass.doSomething(myRetriever.get(myClass));
}
}
I'm a .NET guy, so let me first assert my understanding of a few Java concepts - correct me if I'm wrong.
Java Generics support the concept of bounded wildcards:
class GenericClass< ? extends IInterface> { ... }
...which is similar to the .NET where restriction:
class GenericClass<T> where T: IInterface { ... }
Java's Class class describes a type, and is roughly equivalent to .NET Type class
So far, so good. But I can't find a close enough equivalence to the Java genericly typed Class<T> where T is a bounded wildcard. This basically imposes a restriction on the types that the Class represents.
Let me give an example in Java.
String custSortclassName = GetClassName(); //only known at runtime,
// e.g. it can come from a config file
Class<? extends IExternalSort> customClass
= Class.forName("MyExternalSort")
.asSubclass(IExternalSort.class); //this checks for correctness
IExternalSort impl = customClass.newInstance(); //look ma', no casting!
The closest I could get in .NET is something like this:
String custSortclassName = GetClassName(); //only known at runtime,
// e.g. it can come from a config file
Assembly assy = GetAssembly(); //unimportant
Type customClass = assy.GetType(custSortclassName);
if(!customClass.IsSubclassOf(typeof(IExternalSort))){
throw new InvalidOperationException(...);
}
IExternalSort impl = (IExternalSort)Activator.CreateInstance(customClass);
The Java version looks cleaner to me.
Is there a way to improve the .NET counterpart ?
Using extension methods & a custom wrapper class for System.Type, you can get pretty close to the Java syntax.
NOTE: Type.IsSubclassOf cannot be used to test if a type implements an interface - see the linked documentation on MSDN. One can use Type.IsAssignableFrom instead - see the code below.
using System;
class Type<T>
{
readonly Type type;
public Type(Type type)
{
// Check for the subtyping relation
if (!typeof(T).IsAssignableFrom(type))
throw new ArgumentException("The passed type must be a subtype of " + typeof(T).Name, "type");
this.type = type;
}
public Type UnderlyingType
{
get { return this.type; }
}
}
static class TypeExtensions
{
public static Type<T> AsSubclass<T>(this System.Type type)
{
return new Type<T>(type);
}
}
// This class can be expanded if needed
static class TypeWrapperExtensions
{
public static T CreateInstance<T>(this Type<T> type)
{
return (T)Activator.CreateInstance(type.UnderlyingType);
}
}
Further improvements using interface variance
(Should only be used in production code after the performance has been evaluated. Could be improved by using a (concurrent!) cache dictionary ConcurrentDictionary<System.Type, IType<object>)
Using Covariant type parameters, a feature introduced with C# 4.0, and an additional type interface IType<out T>, which Type<T> implements, one could make things like the following possible:
// IExternalSortExtended is a fictional interface derived from IExternalSort
IType<IExternalSortExtended> extendedSort = ...
IType<IExternalSort> externalSort = extendedSort; // No casting here, too.
One could even do:
using System;
interface IType<out T>
{
Type UnderlyingType { get; }
}
static class TypeExtensions
{
private class Type<T> : IType<T>
{
public Type UnderlyingType
{
get { return typeof(T); }
}
}
public static IType<T> AsSubclass<T>(this System.Type type)
{
return (IType<T>)Activator.CreateInstance(
typeof(Type<>).MakeGenericType(type)
);
}
}
static class TypeWrapperExtensions
{
public static T CreateInstance<T>(this IType<T> type)
{
return (T)Activator.CreateInstance(type.UnderlyingType);
}
}
So that one can (explicitly) cast between unrelated interfaces InterfaceA and InterfaceB like:
var x = typeof(ConcreteAB).AsSubclass<InterfaceA>();
var y = (IType<InterfaceB>)x;
but that kinda defeats the purpose of the exercise.
C# generics is declaration-site variance, the variance of a type parameter is fixed.
Java is use-site variance, so once we have a declaration List<E>, we can use it 3 ways
List<Number> // invariant, read/write
List<+Number> // covariant, read only
List<-NUmber> // contravariant, write only
There are pros and cons to both approaches. The use-site approach is apparently more powerful, though it gained the reputation as being too difficult to programmers. I think it is actually pretty easy to grasp
List<Integer> integers = ...;
List<+Number> numbers = integers; // covariant
Unfortunately, Java invented an absolutely hideous syntax,
List<? extends Number> // i.e. List<+Number>
once your code has several of these it becomes really ugly. You have to learn to get over it.
Now, in the declaration-site camp, how do we achieve 3 variances on the same class? By having more types - a ReadOnlyList<out E>, a WriteOnlyList<in E>, and a List<E> extending both. This is not too bad, and one might say it's a better design. But it may become ugly if there are more type parameters. And if the designer of a class did not anticipate it being used variantly, the users of the class have no way to use it variantly.
You can get a slightly prettier version using the "as" operator:
String custSortclassName = GetClassName();
Assembly assy = GetAssembly();
Type customClass = assy.GetType(custSortclassName);
IExternalSort impl = Activator.CreateInstance(customClass) as IExternalSort;
if(impl==null) throw new InvalidOperationException(...);
But here I'm creating the instance before checking its type, which may be an issue for you.
You can try writing an extension method like the following:
static class TypeExtension
{
public static I NewInstanceOf<I>(this Type t)
where I: class
{
I instance = Activator.CreateInstance(t) as I;
if (instance == null)
throw new InvalidOperationException();
return instance;
}
}
Which can then be used in the following manner:
String custSortclassName = GetClassName(); //only known at runtime,
// e.g. it can come from a config file
Assembly assy = GetAssembly();
Type customClass = assy.GetType(custSortclassName);
IExternalSort impl = customClass.NewInstanceOf<IExternalSort>();
I'm trying to build an SDK using an adapter pattern. Here's what I've got so far:
interface Adapter<T> {
void doWork(WorkUnit<T> unit);
Class<T> getT();
}
class WorkUnit<T> {
public int getId() { ... }
public T getExtras() { ... }
}
class OldWorkUnit {
public <T> void setExtra(T data) { /* Store data in Map<Class, Object> */ }
public <T> WorkUnit<T> toNewWorkUnit(Adapter<T> adapter) { /* Map.get(adapter.getT()) */ }
}
There's a good amount of generics in there, but I can't know T at compile time, and there may be multiple Adapters, all with different types of T. This is meant to be exposed to third parties, so I also need as little implementation as possible in the interface implementation, and it has to be an interface (no abstract class).
Now I want to take this and call doWork with a WorkUnit. My first pass at the code looks like this:
class FooAdapter implements Adapter<FooWorkUnit> {
...
}
OldWorkUnit w = new OldWorkUnit();
w.setExtra(new FooWorkUnit());
Adapter<?> a = new FooAdapter();
WorkUnit<?> unit = w.toNewWorkUnit(a);
a.doWork(unit);
Uh oh, that doesn't compile:
The method doWork(WorkUnit<capture#2-of ?>) in the type Adapter<capture#2-of ?>
is not applicable for the arguments (WorkUnit<capture#4-of ?>)
I know that the WorkUnit generic argument is the same type as the Adapter.doWork() generic argument, but without knowing the type I can't cast it appropriately.
So is there a way to work my way through this?
Why do you wildcard the template class here?:
Adapter<?> a = new FooAdapter();
WorkUnit<?> unit = w.toNewWorkUnit(a);
If you do this:
Adapter<FooWorkUnit> a = new FooAdapter();
WorkUnit<FooWorkUnit> unit = w.toNewWorkUnit(a);
Doesn't that preserve what you need?
Now the compiler knows that FooWorkUnit is the common denominator, so to speak.
Edit: OK point taken about the runtime variability. How about strongly-typing the method that does the work, so that the wildcards are eliminated, but still consistent:
#SuppressWarnings("unchecked")
public <X> void prepareArgsAndDoTheWork() {
OldWorkUnit w = new OldWorkUnit();
w.setExtra(new FooWorkUnit());
Adapter<X> a = (Adapter<X>) ... ; // Obtain the Adapter by reflection etc
WorkUnit<X> unit = w.toNewWorkUnit(a);
a.doWork(unit);
}
Is any practical way to reference a method on a class in a type-safe manner? A basic example is if I wanted to create something like the following utility function:
public Result validateField(Object data, String fieldName,
ValidationOptions options) { ... }
In order to call it, I would have to do:
validateField(data, "phoneNumber", options);
Which forces me to either use a magic string, or declare a constant somewhere with that string.
I'm pretty sure there's no way to get around that with the stock Java language, but is there some kind of (production grade) pre-compiler or alternative compiler that may offer a work around? (similar to how AspectJ extends the Java language) It would be nice to do something like the following instead:
public Result validateField(Object data, Method method,
ValidationOptions options) { ... }
And call it with:
validateField(data, Person.phoneNumber.getter, options);
As others mention, there is no real way to do this... and I've not seen a precompiler that supports it. The syntax would be interesting, to say the least. Even in your example, it could only cover a small subset of the potential reflective possibilities that a user might want to do since it won't handle non-standard accessors or methods that take arguments, etc..
Even if it's impossible to check at compile time, if you want bad code to fail as soon as possible then one approach is to resolve referenced Method objects at class initialization time.
Imagine you have a utility method for looking up Method objects that maybe throws error or runtime exception:
public static Method lookupMethod( Class c, String name, Class... args ) {
// do the lookup or throw an unchecked exception of some kind with a really
// good error message
}
Then in your classes, have constants to preresolve the methods you will use:
public class MyClass {
private static final Method GET_PHONE_NUM = MyUtils.lookupMethod( PhoneNumber.class, "getPhoneNumber" );
....
public void someMethod() {
validateField(data, GET_PHONE_NUM, options);
}
}
At least then it will fail as soon as MyClass is loaded the first time.
I use reflection a lot, especially bean property reflection and I've just gotten used to late exceptions at runtime. But that style of bean code tends to error late for all kinds of other reasons, being very dynamic and all. For something in between, the above would help.
There isn't anything in the language yet - but part of the closures proposal for Java 7 includes method literals, I believe.
I don't have any suggestions beyond that, I'm afraid.
Check out https://proxetta.jodd.org/refs/methref. It uses the Jodd proxy library (Proxetta) to proxy your type. Not sure about its performance characteristics, but it does provide type safety.
An example: Suppose Str.class has method .boo(), and you want to get its name as the string "boo":
String methodName = Methref.of(Str.class).name(Str::boo);
There's more to the API than the example above: https://oblac.github.io/jodd-site/javadoc/jodd/methref/Methref.html
Is any practical way to reference a method on a class in a type-safe manner?
First of all, reflection is type-safe. It is just that it is dynamically typed, not statically typed.
So, assuming that you want a statically typed equivalent of reflection, the theoretical answer is that it is impossible. Consider this:
Method m;
if (arbitraryFunction(obj)) {
m = obj.getClass().getDeclaredMethod("foo", ...);
} else {
m = obj.getClass().getDeclaredMethod("bar", ...);
}
Can we do this so that that runtime type exceptions cannot happen? In general NO, since this would entail proving that arbitraryFunction(obj) terminates. (This is equivalent to the Halting Problem, which is proven to be unsolvable in general, and is intractable using state-of-the-art theorem proving technology ... AFAIK.)
And I think that this road-block would apply to any approach where you could inject arbitrary Java code into the logic that is used to reflectively select a method from an object's class.
To my mind, the only moderately practical approach at the moment would be to replace the reflective code with something that generates and compiles Java source code. If this process occurs before you "run" the application, you've satisfied the requirement for static type-safety.
I was more asking about reflection in which the result is always the same. I.E. Person.class.getMethod("getPhoneNumber", null) would always return the same method and it's entirely possible to resolve it at compile time.
What happens if after compiling the class containing this code, you change Person to remove the getPhoneNumber method?
The only way you can be sure that you can resolve getPhoneNumber reflectively is if you can somehow prevent Person from being changed. But you can't do that in Java. Runtime binding of classes is a fundamental part of the language.
(For record, if you did that for a method that you called non-reflectively, you would get an IncompatibleClassChangeError of some kind when the two classes were loaded ...)
It has been pointed out that in Java 8 and later you could declare your validator something like this:
public Result validateField(Object data,
SomeFunctionalInterface function,
ValidationOptions options) { ... }
where SomeFunctionalInterface corresponds to the (loosely speaking) common signature of the methods you are validating.
Then you can call it with a method reference; e.g.
validateField(data, SomeClass::someMethod, options)
This is approach is statically type-safe. You will get a compilation error if SomeClass doesn't have someMethod or if it doesn't conform to SomeFunctionalInterface.
But you can't use a string to denote the method name. Looking up a method by name would entail either reflection ... or something else that side-steps static (i.e. compile time / load time) type safety.
Java misses the syntax sugar to do something as nice as Person.phoneNumber.getter. But if Person is an interface, you could record the getter method using a dynamic proxy. You could record methods on non-final classes as well using CGLib, the same way Mockito does it.
MethodSelector<Person> selector = new MethodSelector<Person>(Person.class);
selector.select().getPhoneNumber();
validateField(data, selector.getMethod(), options);
Code for MethodSelector: https://gist.github.com/stijnvanbael/5965609
Inspired by mocking frameworks, we could dream up the following syntax:
validator.validateField(data, options).getPhoneNumber();
Result validationResult = validator.getResult();
The trick is the generic declaration:
class Validator {
public <T> T validateField(T data, options) {...}
}
Now the return type of the method is the same as your data object's type and you can use code completion (and static checking) to access all the methods, including the getter methods.
As a downside, the code isn't quite intuitive to read, since the call to the getter doesn't actually get anything, but instead instructs the validator to validate the field.
Another possible option would be to annotate the fields in your data class:
class FooData {
#Validate(new ValidationOptions(...))
private PhoneNumber phoneNumber;
}
And then just call:
FooData data;
validator.validate(data);
to validate all fields according to the annotated options.
The framework picklock lets you do the following:
class Data {
private PhoneNumber phoneNumber;
}
interface OpenData {
PhoneNumber getPhoneNumber(); //is mapped to the field phoneNumber
}
Object data = new Data();
PhoneNumber number = ObjectAccess
.unlock(data)
.features(OpenData.class)
.getPhoneNumber();
This works in a similar way setters and private methods. Of course, this is only a wrapper for reflection, but the exception does not occur at unlocking time not at call time. If you need it at build time, you could write a unit test with:
assertThat(Data.class, providesFeaturesOf(OpenData.class));
I found a way to get the Method instance using Lambdas. It works only on interface methods though currently.
It works using net.jodah:typetools which is a very lightweight library.
https://github.com/jhalterman/typetools
public final class MethodResolver {
private interface Invocable<I> {
void invokeWithParams(I instance, Class<?>[] parameterTypes) throws Throwable;
}
interface ZeroParameters<I, R> extends Invocable<I> {
R invoke(I instance) throws Throwable;
#Override
default void invokeWithParams(I instance, Class<?>[] parameterTypes) throws Throwable {
invoke(instance);
}
}
public static <I, R> Method toMethod0(ZeroParameters<I, R> call) {
return toMethod(ZeroParameters.class, call, 1);
}
interface OneParameters<I, P1, R> extends Invocable<I> {
R invoke(I instance, P1 p1) throws Throwable;
#Override
default void invokeWithParams(I instance, Class<?>[] parameterTypes) throws Throwable {
invoke(instance, param(parameterTypes[1]));
}
}
public static <I, P1, R> Method toMethod1(OneParameters<I, P1, R> call) {
return toMethod(OneParameters.class, call, 2);
}
interface TwoParameters<I, P1, P2, R> extends Invocable<I> {
R invoke(I instance, P1 p1, P2 p2) throws Throwable;
#Override
default void invokeWithParams(I instance, Class<?>[] parameterTypes) throws Throwable {
invoke(instance, param(parameterTypes[1]), param(parameterTypes[2]));
}
}
public static <I, P1, P2, R> Method toMethod2(TwoParameters<I, P1, P2, R> call) {
return toMethod(TwoParameters.class, call, 3);
}
private static final Map<Class<?>, Object> parameterMap = new HashMap<>();
static {
parameterMap.put(Boolean.class, false);
parameterMap.put(Byte.class, (byte) 0);
parameterMap.put(Short.class, (short) 0);
parameterMap.put(Integer.class, 0);
parameterMap.put(Long.class, (long) 0);
parameterMap.put(Float.class, (float) 0);
parameterMap.put(Double.class, (double) 0);
}
#SuppressWarnings("unchecked")
private static <T> T param(Class<?> type) {
return (T) parameterMap.get(type);
}
private static <I> Method toMethod(Class<?> callType, Invocable<I> call, int responseTypeIndex) {
Class<?>[] typeData = TypeResolver.resolveRawArguments(callType, call.getClass());
Class<?> instanceClass = typeData[0];
Class<?> responseType = responseTypeIndex != -1 ? typeData[responseTypeIndex] : Void.class;
AtomicReference<Method> ref = new AtomicReference<>();
I instance = createProxy(instanceClass, responseType, ref);
try {
call.invokeWithParams(instance, typeData);
} catch (final Throwable e) {
throw new IllegalStateException("Failed to call no-op proxy", e);
}
return ref.get();
}
#SuppressWarnings("unchecked")
private static <I> I createProxy(Class<?> instanceClass, Class<?> responseType,
AtomicReference<Method> ref) {
return (I) Proxy.newProxyInstance(MethodResolver.class.getClassLoader(),
new Class[] {instanceClass},
(proxy, method, args) -> {
ref.set(method);
return parameterMap.get(responseType);
});
}
}
Usage:
Method method = MethodResolver.toMethod2(SomeIFace::foobar);
System.out.println(method); // public abstract example.Result example.SomeIFace.foobar(java.lang.String,boolean)
Method get = MethodResolver.<Supplier, Object>toMethod0(Supplier::get);
System.out.println(get); // public abstract java.lang.Object java.util.function.Supplier.get()
Method accept = MethodResolver.<IntFunction, Integer, Object>toMethod1(IntFunction::apply);
System.out.println(accept); // public abstract java.lang.Object java.util.function.IntFunction.apply(int)
Method apply = MethodResolver.<BiFunction, Object, Object, Object>toMethod2(BiFunction::apply);
System.out.println(apply); // public abstract java.lang.Object java.util.function.BiFunction.apply(java.lang.Object,java.lang.Object)
Unfortunately you have to create a new interface and method based on the parameter count and whether the method returns void or not.
However, if you have a somewhat fixed/limited method signature/parameter types, then this becomes quite handy.