Related
I am creating a store for user preferences, and there are a fixed number of preferences that users can set values for. The names of the preferences (settings) are stored as an Enum:
public enum UserSettingName {
FOO,
BAR,
ETC
}
What I would like to be able to do is store a value type with the name so that the service will store the user's value with the correct Java type. For example, FOO might be a Long, and BAR might be a String. Up until now, we were storing all values as Strings, and then manually casting the values into the appropriate Java type. This has lead to try/catch blocks everywhere, when it makes more sense to have only one try/catch in the service. I understand that Enums cannot have generic types, so I have been playing around with:
public enum UserSettingName {
FOO(Long.class),
BAR(String.class),
ETC(Baz.class)
private Class type;
private UserSettingName(Class type) {
this.type = type;
}
public Class getType() {
return this.type;
}
}
I have a generic UserSetting object that has public T getSettingValue() and public void setSettingValue(T value) methods that should return and set the value with the correct type. My problem comes from trying to specify that generic type T when I create or retrieve a setting because I can't do something like:
new UserSetting<UserSettingName.FOO.getType()>(UserSettingName.FOO, 123L)
Sorry if this isn't exactly clear, I can try to clarify if it's not understood.
Thanks!
UPDATE
Both the setting name and value are coming in from a Spring MVC REST call:
public ResponseEntity<String> save(#PathVariable Long userId, #PathVariable UserSettingName settingName, #RequestBody String settingValue)
So I used the Enum because Spring casts the incoming data automatically.
Firstly you have to step back and think about what you're trying to achieve, and use a standard pattern or language construct to achieve it.
It's not entirely clear what you're going after here but from your approach it almost certainly looks like you're reinventing something which could be done in a much more straightforward manner in Java. For example, if you really need to know and work with the runtime classes of objects, consider using the reflection API.
On a more practical level - what you're trying to do here isn't possible with generics. Generics are a compile-time language feature - they are useful for avoiding casting everything explicitly from Object and give you type-checking at compilation time. You simply cannot use generics in this way, i.e. setting T as some value UserSettingName.Foo.getType() which is only known at runtime.
Look how it done by netty:
http://netty.io/wiki/new-and-noteworthy.html#type-safe-channeloption
They done it by using typed constants:
http://grepcode.com/file/repo1.maven.org/maven2/io.netty/netty-all/4.0.0.Beta1/io/netty/channel/ChannelOption.java#ChannelOption
EDIT:
public interface ChannelConfig {
...
<T> boolean setOption(ChannelOption<T> option, T value);
...
}
public class ChannelOption<T> ...
public static final ChannelOption<Integer> SO_TIMEOUT =
new ChannelOption<Integer>("SO_TIMEOUT");
...
}
EDIT2: you can transform it like:
class Baz {}
class UserSettingName<T> {
public static final UserSettingName<Baz> ETC = new UserSettingName<Baz>();
}
class UserSetting {
public <T> UserSetting(UserSettingName<T> name, T param) {
}
}
public class Test {
public static void main(String[] args) {
new UserSetting(UserSettingName.ETC, new Baz());
}
}
Enums are not the answer here. If you find yourself repeating code everywhere you could just create a utility class and encapsulate all the try/catch logic there. That would cut down on your code redundancy without majorly impacting your current code.
public class Util
{
public static MyObject getObjectFromString(String s)
{
try
{
return (MyObject)s;
}
catch(Exception e)
{
return null;
}
}
}
Then use as follows:
MyObject myObj = Util.getObjectFromString(string);
I'm trying to genericize a factory method that returns
a generic Base class. It works, but I'm getting the
"BaseClass is a raw type..." warning.
I've read through the Java docs on Generic methods,
but I'm still not quite getting how to accomplish this.
Here's some code:
Class #1
//base abstract class
public abstract class BaseFormatter<T>
{
public abstract String formatValue(T value);
}
Class #2
//two implementations of concrete classes
public class FooFormatter extends BaseFormatter<Integer>
{
#Override
public String formatValue(Integer value)
{
//return a formatted String
}
}
Class #3
public class BarFormatter extends BaseFormatter<String>
{
#Override
public String formatValue(String value)
{
//return a formatted String
}
}
Factory Method in a separate class
public static BaseFormatter getFormatter(Integer unrelatedInteger)
{
if (FOO_FORMATTER.equals(unrelatedInteger))
return new FooFormatter();
else if (BAR_FORMATTER.equals(unrelatedInteger))
return new BarFormatter();
//else...
}
Call to the Factory Method from elsewhere in the code
BaseFormatter<Integer> formatter = getFormatter(someInteger);
formatter.formatValue(myIntegerToFormat);
The problem is the getFormatter() method warns that BaseFormatter is
a raw type, which it is. I've tried various things like BaseFormatter
et al. I, of course, want the return type to be generic, as in the declared
BaseFormatter in the calling method.
Note that the formatter type is not based on class type. e.g. not all Integer
values are formatted with a FooFormatter. There are two or three different
ways an Integer (or String, or List) can be formatted. That's what the
param unrelatedInteger is for.
Thanks in advance for any feedback.
If getFormatter is defined in BaseFormatter, then use:
public static BaseFormatter<T> getFormatter(Integer unrelatedInteger)
If getFormatter is defined in another class than BaseFormatter, then use:
public static BaseFormatter<?> getFormatter(Integer unrelatedInteger)
You're actuaaly saying that there's no connection between the typed parameter of BaseFormatter and the unrelatedInteger that is passed as argument to the getFormatter method.
I get some other warning:
Uncehcked Assignment: BaseFormatter to BaseFormatter<Integer>
This warning is worse than the one you indicated. It warns that this user code might try to insert a BaseFormatter<String> into BaseFormatter<Integer>, something that will be noticed only when fails in runtime... Consider a user accidentally uses you factory method like such:
BaseFormatter<Integer> myUnsafeFormatter =
FormatterFactory.getFormatter(unrelatedIntegerForBarFormatter);
The compiler cannot relate the unrelatedInteger with the parameterized type of the returned BaseFormatter.
Alternitavely, I'd let the user explicitly use the concrete formatter constructors. Any common code shared by all formatters could be put into FormatterUtils class (just don't let that utils class to grow to much...).
Some type systems in academic languages can express a so-called dependent sum. Java certainly cannot; so what, sensibly, could be the type of the object returned by the getFormatter method? The best we can do is BaseFormatter< ? extends Object >, or BaseFormatter< ? > for short, as Integer and String have only Object in common.
I think the original post begs the question, why must we use an integer to decide what formatter to return, and if the type of formatter would not be known by the caller, why would the caller need a stronger variable type than BaseFormatter< ? >?
I'm trying to make a method that takes an argument of Country.class, User.class etc, and returns argument.count().
All the possible classes that I would give to this method extend from Model and have the method count().
My code:
private static long <T> countModel(Model<T> clazz)
{
// there is other important stuff here, which prevents me from
// simply by-passing the method altogether.
return clazz.count();
}
Called by:
renderArgs.put("countryCount", countModel(Country.class));
However this just doesn't work at all.
How do I do this, please?
I think you want to do
private long countModel(Class<? extends Model> clazz) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException
{
Method countMethod = clazz.getDeclaredMethod("count", null);
return (Long) countMethod.invoke(null, null);
}
Hopefully something like this would work (my reflection skills are not really that good).
Don't fully understand what you are trying to achieve. Did you mean this?
private static long <T> countModel(Model<T> model)
{
return model.count();
}
renderArgs.put("countryCount", countModel(country));
EDIT: If count is a static method, it has nothing to do with model. The static method is not inherited. So all you have to do is to call it directly,
renderArgs.put("countryCount", Country.count());
Clarifying, you want a class (A) that is constrained to have a particular class method (B) and you want to pass that class as an argument to some other method (C) and have that method (C) invoke that class method on that class (A.B())?
The first part, the type constraint, that can't be done. Java's type system just does not work that way.
The second part, passing a class as an argument and invoking a class method on it, that can be done using reflection. This is how to do it, correcting from your code (though you should be more careful with the exceptions than I've been in this).
private static <T extends Model> long countModel(Class<T> clazz) throws Exception
{
return (Long) clazz.getMethod("count").invoke(null);
}
The null is the instance to invoke this on (no instance; it's a class method). The cast to Long is required as the result of invoke() is an Object. The type parameter must go before the result type. And the whole thing can take any class that is a subclass of Model as a parameter; it will just fail at runtime if the count method isn't present. Them's the breaks.
(Also note that if you wanted to pass arguments to count(), you'd have to specify the classes of those arguments to getMethod and the values themselves to invoke, in both cases as subsequent arguments. Both support Java5 variable argument lists.)
In the line
renderArgs.put("countryCount", countModel(Country.class));
you call countModel with a Class<Country>, but you have to call it with an instance of Country like this:
Country country = new Country();
renderArgs.put("countryCount", countModel( country );
In reply to your comment to ZZ Coder; a static method in Java is called in the namespace context of a class, like Model.count() for a static method count() in the class Model, but the method does not become part of Model.class, Model.class is an instance of Class describing the class Model. (I can see where the confusion originates, it would be logical to have a specialised Model.class that includes the static methods, but Java isn't desinged that way.)
Your way out is to use reflection to call the static count() for the class that you pass to your code.
You are not passing an instance of country here, you are passing a Class object:
renderArgs.put("countryCount", countModel(Country.class));
You need to instantiate A model and pass it as an argument:
Model model = new Country();
renderArgs.put("countryCount", countModel(model));
or
Country country = new Country();
renderArgs.put("countryCount", countModel(country));
In this case, Country.class is an object of the Class<Country> type.
You are passing Country.class which is a Class object. How is it a Model object?
I would like to create an object of Generics Type in java. Please suggest how can I achieve the same.
Note: This may seem a trivial Generics Problem. But I bet.. it isn't. :)
suppose I have the class declaration as:
public class Abc<T> {
public T getInstanceOfT() {
// I want to create an instance of T and return the same.
}
}
public class Abc<T> {
public T getInstanceOfT(Class<T> aClass) {
return aClass.newInstance();
}
}
You'll have to add exception handling.
You have to pass the actual type at runtime, since it is not part of the byte code after compilation, so there is no way to know it without explicitly providing it.
In the code you posted, it's impossible to create an instance of T since you don't know what type that is:
public class Abc<T>
{
public T getInstanceOfT()
{
// There is no way to create an instance of T here
// since we don't know its type
}
}
Of course it is possible if you have a reference to Class<T> and T has a default constructor, just call newInstance() on the Class object.
If you subclass Abc<T> you can even work around the type erasure problem and won't have to pass any Class<T> references around:
import java.lang.reflect.ParameterizedType;
public class Abc<T>
{
T getInstanceOfT()
{
ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass();
Class<T> type = (Class<T>) superClass.getActualTypeArguments()[0];
try
{
return type.newInstance();
}
catch (Exception e)
{
// Oops, no default constructor
throw new RuntimeException(e);
}
}
public static void main(String[] args)
{
String instance = new SubClass().getInstanceOfT();
System.out.println(instance.getClass());
}
}
class SubClass
extends Abc<String>
{
}
What you wrote doesn't make any sense, generics in Java are meant to add the functionality of parametric polymorphism to objects.
What does it mean? It means that you want to keep some type variables of your classes undecided, to be able to use your classes with many different types.
But your type variable T is an attribute that is resolved at run-time, the Java compiler will compile your class proving type safety without trying to know what kind of object is T so it's impossible for it to let your use a type variable in a static method. The type is associated to a run-time instance of the object while public void static main(..) is associated to the class definition and at that scope T doesn't mean anything.
If you want to use a type variable inside a static method you have to declare the method as generic (this because, as explained type variables of a template class are related to its run-time instance), not the class:
class SandBox
{
public static <T> void myMethod()
{
T foobar;
}
}
this works, but of course not with main method since there's no way to call it in a generic way.
EDIT: The problem is that because of type erasure just one generic class is compiled and passed to JVM. Type checker just checks if code is safe, then since it proved it every kind of generic information is discarded.
To instantiate T you need to know the type of T, but it can be many types at the same time, so one solution with requires just the minimum amount of reflection is to use Class<T> to instantiate new objects:
public class SandBox<T>
{
Class<T> reference;
SandBox(Class<T> classRef)
{
reference = classRef;
}
public T getNewInstance()
{
try
{
return reference.newInstance();
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public static void main(String[] args)
{
SandBox<String> t = new SandBox<String>(String.class);
System.out.println(t.getNewInstance().getClass().getName());
}
}
Of course this implies that the type you want to instantiate:
is not a primitive type
it has a default constructor
To operate with different kind of constructors you have to dig deeper into reflection.
You need to get the type information statically. Try this:
public class Abc<T> {
private Class<T> clazz;
public Abc(Class<T> clazz) {
this.clazz = clazz;
}
public T getInstanceOfT()
throws throws InstantiationException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException,
NoSuchMethodException,
SecurityException {
return clazz.getDeclaredConstructor().newInstance();
}
}
Use it as such:
Abc<String> abc = new Abc<String>(String.class);
abc.getInstanceOfT();
Depending on your needs, you may want to use Class<? extends T> instead.
The only way to get it to work is to use Reified Generics. And this is not supported in Java (yet? it was planned for Java 7, but has been postponed). In C# for example it is supported assuming that T has a default constructor. You can even get the runtime type by typeof(T) and get the constructors by Type.GetConstructor(). I don't do C# so the syntax may be invalid, but it roughly look like this:
public class Foo<T> where T:new() {
public void foo() {
T t = new T();
}
}
The best "workaround" for this in Java is to pass a Class<T> as method argument instead as several answers already pointed out.
First of all, you can't access the type parameter T in the static main method, only on non-static class members (in this case).
Second, you can't instantiate T because Java implements generics with Type Erasure. Almost all the generic information is erased at compile time.
Basically, you can't do this:
T member = new T();
Here's a nice tutorial on generics.
You don't seem to understand how Generics work.
You may want to look at http://java.sun.com/j2se/1.5.0/docs/guide/language/generics.html
Basically what you could do is something like
public class Abc<T>
{
T someGenericThing;
public Abc(){}
public T getSomeGenericThing()
{
return someGenericThing;
}
public static void main(String[] args)
{
// create an instance of "Abc of String"
Abc<String> stringAbc = new Abc<String>();
String test = stringAbc.getSomeGenericThing();
}
}
I was implementing the same using the following approach.
public class Abc<T>
{
T myvar;
public T getInstance(Class<T> clazz) throws InstantiationException, IllegalAccessException
{
return clazz.newInstance();
}
}
I was trying to find a better way to achieve the same.
Isn't it possible?
Type Erasure Workaround
Inspired by #martin's answer, I wrote a helper class that allows me to workaround the type erasure problem. Using this class (and a little ugly trick) I'm able to create a new instance out of a template type:
public abstract class C_TestClass<T > {
T createTemplateInstance() {
return C_GenericsHelper.createTemplateInstance( this, 0 );
}
public static void main( String[] args ) {
ArrayList<String > list =
new C_TestClass<ArrayList<String > >(){}.createTemplateInstance();
}
}
The ugly trick here is to make the class abstract so the user of the class is forced to subtype it. Here I'm subclassing it by appending {} after the call to the constructor. This defines a new anonymous class and creates an instance of it.
Once the generic class is subtyped with concrete template types, I'm able to retrieve the template types.
public class C_GenericsHelper {
/**
* #param object instance of a class that is a subclass of a generic class
* #param index index of the generic type that should be instantiated
* #return new instance of T (created by calling the default constructor)
* #throws RuntimeException if T has no accessible default constructor
*/
#SuppressWarnings( "unchecked" )
public static <T> T createTemplateInstance( Object object, int index ) {
ParameterizedType superClass =
(ParameterizedType )object.getClass().getGenericSuperclass();
Type type = superClass.getActualTypeArguments()[ index ];
Class<T > instanceType;
if( type instanceof ParameterizedType ) {
instanceType = (Class<T > )( (ParameterizedType )type ).getRawType();
}
else {
instanceType = (Class<T > )type;
}
try {
return instanceType.newInstance();
}
catch( Exception e ) {
throw new RuntimeException( e );
}
}
}
There are hacky ways around this when you really have to do it.
Here's an example of a transform method that I find very useful; and provides one way to determine the concrete class of a generic.
This method accepts a collection of objects as input, and returns an array where each element is the result of calling a field getter on each object in the input collection. For example, say you have a List<People> and you want a String[] containing everyone's last name.
The type of the field value returned by the getter is specified by the generic E, and I need to instantiate an array of type E[] to store the return value.
The method itself is a bit ugly, but the code you write that uses it can be so much cleaner.
Note that this technique only works when somewhere in the input arguments there is an object whose type matches the return type, and you can deterministically figure it out. If the concrete classes of your input parameters (or their sub-objects) can tell you nothing about the generics, then this technique won't work.
public <E> E[] array (Collection c) {
if (c == null) return null;
if (c.isEmpty()) return (E[]) EMPTY_OBJECT_ARRAY;
final List<E> collect = (List<E>) CollectionUtils.collect(c, this);
final Class<E> elementType = (Class<E>) ReflectionUtil.getterType(c.iterator().next(), field);
return collect.toArray((E[]) Array.newInstance(elementType, collect.size()));
}
Full code is here: https://github.com/cobbzilla/cobbzilla-utils/blob/master/src/main/java/org/cobbzilla/util/collection/FieldTransformer.java#L28
It looks like you are trying to create the class that serves as the entry point to your application as a generic, and that won't work... The JVM won't know what type it is supposed to be using when it's instantiated as you start the application.
However, if this were the more general case, then something like would be what you're looking for:
public MyGeneric<MyChoiceOfType> getMeAGenericObject(){
return new MyGeneric<MyChoiceOfType>();
}
or perhaps:
MyGeneric<String> objMyObject = new MyGeneric<String>();
Abc<String> abcInstance = new Abc<String> ();
..for example
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.