0

I want to create some utility method and I want it to be able to work on different objects as long as they can provide an Integer property that is read/write.

I know the "Standard" way would be to:

  1. declare some interface IProvidesAccessToInteger that has setInteger and getInteger.
  2. declare MyUtility.doSomethingWonderful(IProvidesAccessToInteger obj)
  3. make calls to obj.setInteger and obj.getInteger.

But this has a very strong downside that it requires the cooperation of all those classes that I want MyUtility to work with and not only that but even if some class wants to cooperate MyUtility would still only be able to .doSomethingWonderful() to just a single predetermined field of that classes.

I am looking for some syntax that would allow for something like MyUtility.doSomethingWonderful(T obj, Method<Integer> getter, Method<Integer,Integer> setter) maybe using generics somehow to specify the requirement that objhas two methods that get set and set an Integer and have some way to call them on the instance of obj.

It might also be interesting to do something similar with static methods that do not need an object.

UPDATE: As I was pointed to reflection, I want to clarify that I know close things can be done using reflection.

However since I don't need to resolve the actual interface in runtime I had the hope that JAVA has some way to specify sort of an "Interface fulfilment map" such that If my requirement would be an object that has two methods int ?() and void ?(int) I could specify something like .doSomethingWonderful(?<int getter(),void setter(int)> obj) and call it once with some object1 that has int getInt() and void setInt(int) and once with some other object2 that has int getIntValue() and void setIntValue(int) by specifying in the calls that object fulfills the requirements for getInteger by getInt and fulfills the requirements for setInteger by setInt and so on. maybe with a call syntax like `.doSomethingWonderful((?)object1)

At least in theory I think it should be possible to do all at compile time and not require any runtime reflection.

maybe the right name for this would by an anonymous interface.

that said, I accept that a runtime solution via reflection might also be a solution.

3
  • You just described reflection. Now google it and read! Commented Oct 31, 2013 at 8:53
  • I know whats reflection, but I am not looking for the power to resolve the getter and setter at RUNTIME so I was looking (hopefully) for a more declarative (and typesafe) way to do this. Commented Oct 31, 2013 at 8:58
  • No, I think (1) using interfaces (which you said you don't want to do) and (2) Boris's excellent solution are really your only options. Commented Oct 31, 2013 at 9:00

3 Answers 3

1

The thing closest to your description will come with Java 8. You still need interfaces but the caller does not need to care about them and even better, there are plenty of default interfaces for typical tasks. For example, you can define a method like this:

static void increment(IntSupplier in, IntConsumer out)
{
  out.accept(in.getAsInt()+1);
}

and use it like that to access different properties of an object:

class ClassWithInProperties {
  int a, b;

  public int getA() {
    return a;
  }
  public void setA(int a) {
    this.a = a;
  }
  public int getB() {
    return b;
  }
  public void setB(int b) {
    this.b = b;
  }
  @Override
  public String toString() {
    return "a="+a+", b="+b;
  }
}

ClassWithInProperties obj=new ClassWithInProperties();
increment(obj::getA, obj::setA);
increment(obj::getA, obj::setA);
increment(obj::getB, obj::setB);
System.out.println(obj);

or with static methods:

public class Test {
    static int DATA = 42;
    static int getData() {
        return DATA;
    }
    static void setData(int i) {
        DATA=i;
    }
}

increment(Test::getData, Test::setData);
System.out.println(DATA);
Sign up to request clarification or add additional context in comments.

3 Comments

And where do IntSupplier, IntConsumer, getAsInt and accept come from? what makes obj::getA suitable for use as an IntSupplier ? and what if I want to get a method that task two Integers and a String ?
@epeleg this is all Java 8. IntSupplier and IntConsumer are part of the Java 8 API. obj::getA is an example of a Java 8 Lambda.
@epeleg as said they are predefined in Java 8 and the method reference is suitable because its signature matches. For special cases like your three parameter method you will need to define your own interface but the method reference works the same when the referred method has the right signature for your interface. download.java.net/jdk8/docs/api/java/lang/…
1

Your getter and setter are pretty Paradigm of reflection. But that would be import much risk and lose performance. Interface Oriented Programming are pretty common "standard" to handle this scenario.

Comments

1

You cannot do this with generics.

You can do this with reflection. Using a utility such as BeanUtils would of course be easier, but you can write it by hand too.

public void doSomethingWonderful(final Object in, final String fieldName) 
        throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    final String upperCased = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    final Method getter = in.getClass().getMethod("get" + upperCased);
    final Method setter = in.getClass().getMethod("set" + upperCased, Integer.TYPE);

    //to invoke getter
    final int val = (int) getter.invoke(in);

    //to invoke setter
    setter.invoke(in, val);
}

I have assumed that you are using an int rather than an Integer, you will need to change the code slightly in the latter case.

You can see that it throws a massive number of exceptions, I would recommend wrapping them all in a custom exception type to simplify client code.

EDIT

Op wants the break down the method into three overloaded methods:

public void doSomethingWonderful(final Object in, final String fieldName)
        throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    final String upperCased = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    doSomethingWonderful(in, "get" + upperCased, "set" + upperCased);
}

public void doSomethingWonderful(final Object in, final String getterName, final String setterName)
        throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    final Method getter = in.getClass().getMethod(getterName);
    final Method setter = in.getClass().getMethod(setterName);
    doSomethingWonderful(in, getter, setter);
}

public void doSomethingWonderful(final Object in, final Method getter, final Method setter)
        throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    //to invoke getter
    final int val = (int) getter.invoke(in);

    //to invoke setter
    setter.invoke(in, val);
}

4 Comments

even if I do go the reflection way, why not pass Method getter and Method setter as parameters? or alternatively use String getterMethodName and String setterMethodName ?
@epeleg Why not pass methods? Because you would need to get them somewhere - this will be duplicated code, why not put it in one place. Why not pass getter and setter names? Because that's three method parameters, two of which can be merged into one - the names are defined relative to the field name. Of course this depends on your use case, but this is the most standard way of writing a generic bean accessor.
I see - this is because I said I wanted "to work on different objects as long as they can provide an Integer property that is read/write". Based on same login If I would have wanted to keep the flexibility to read and write from two different places then you would pass method names... In a scenario where this Utility is used often It might be better performance-wise to be able to let the caller get the methods and cache them for multiple calls - so maybe provide a version using method names and getMethod them and calls a version that already gets Methods which can also be called directly.
@epeleg I have added an example of your suggestion using overloading.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.