I am trying to pass a selected "get"-method of class A to a method in class B. I have checked out Java Pass Method as Parameter, but I was not able to adopt the interface approach to my problem in a reasonable way. I would prefer to not use java 8 (lambdas) and if possible avoid reflection as well. My feeling is, that I am looking at my problem the wrong way. Here is the specific simplified example of what I am trying to accomplish:
I have a class containing some fields and get-methods:
public class DataStore {
private float a;
private float b;
private float c;
public float getA() {
return a;
}
public float getB() {
return b;
}
public float getC() {
return c;
}
}
Next I have my main class instantiating DataStore as Values of a Map and then accessing specific fields of DataStore like:
public class App {
public static void main(String[] args) {
// declare TreeMap using DataStore class as value
Map<Integer, DataStore> dataMap = new TreeMap<Integer, DataStore>();
// populate Map with example data
dataMap.put(2, new DataStore(1f,2f,3f));
dataMap.put(10, new DataStore(3f,4f,5f));
dataMap.put(4, new DataStore(6f,7f,8f));
// work with specific fields in DataStore, e.g. assign to array
float[] aArray = getValuesAsArray(dataMap, DataStore.getA());
float[] bArray = getValuesAsArray(dataMap, DataStore.getB());
float[] cArray = getValuesAsArray(dataMap, DataStore.getC());
}
/**
* Assign specific field of DataStore from Map to Array
* @param dataMap
* @param getVar - reference for specified getter method
* @return
*/
private static float[] getValuesAsArray(Map<Integer, DataStore> dataMap, MethodReference getVar()) {
int i = 0;
int nMap = dataMap.size();
float[] fArray = new float[nMap];
for (Map.Entry<Integer, DataStore> entry : dataMap.entrySet()) {
DataStore ds = entry.getValue();
fArray[i] = ds.getVar();
i++;
}
return fArray;
}
}
Clearly this wont work, as I have to figure out how to pass my selected get method into getValuesAsArray().
Somehow, I guess, my approach may be wrong. So I am open for suggestions.
-
Maybe I'm just being a noob but why do you need to pass a method here? What is this program trying to accomplish?px06– px062016年08月24日 08:41:39 +00:00Commented Aug 24, 2016 at 8:41
-
@px06 - The actual DataStore will have more than 3 fields. I am collecting a data set in that dataMap in a random way. The key of the treeMap is a sparse set of integers associated to each data set. At some point I will need to retrieve or output the individual field values as "sorted" array, where sorting should be done based on the key integers.W.E.Coyote– W.E.Coyote2016年08月24日 09:00:41 +00:00Commented Aug 24, 2016 at 9:00
4 Answers 4
Your getX() methods can be seen as a Function that accepts a DataStore instance and returns a float.
In Java 8 you can represent them with method references :
float[] aArray = getValuesAsArray(dataMap, DataStore::getA);
float[] bArray = getValuesAsArray(dataMap, DataStore::getB);
float[] cArray = getValuesAsArray(dataMap, DataStore::getC);
Then your getValuesAsArray will accept a Function<DataStore,Float> parameter and execute the function :
private static float[] getValuesAsArray(Map<Integer, DataStore> dataMap, Function<DataStore,Float> func) {
int i = 0;
int nMap = dataMap.size();
float[] fArray = new float[nMap];
for (Map.Entry<Integer, DataStore> entry : dataMap.entrySet()) {
DataStore ds = entry.getValue();
fArray[i] = func.apply(ds);
i++;
}
return fArray;
}
Without using Java 8, you can define your own interface that contains a method that accepts a DataStore instance and returns a float. Then, instead of using Java 8 method references, you would have to pass to your getValuesAsArray method an implementation of that interface (you could use an anonymous class instance implementing the interface) which calls one of the getX() methods.
For example :
public interface ValueGetter
{
public float get (DataStore source);
}
float[] aArray = getValuesAsArray(dataMap, new ValueGetter() {public float get (DataStore source) {return source.getA();}});
float[] bArray = getValuesAsArray(dataMap, new ValueGetter() {public float get (DataStore source) {return source.getB();}});
float[] cArray = getValuesAsArray(dataMap, new ValueGetter() {public float get (DataStore source) {return source.getC();}});
And
private static float[] getValuesAsArray(Map<Integer, DataStore> dataMap, ValueGetter func) {
int i = 0;
int nMap = dataMap.size();
float[] fArray = new float[nMap];
for (Map.Entry<Integer, DataStore> entry : dataMap.entrySet()) {
DataStore ds = entry.getValue();
fArray[i] = func.get(ds);
i++;
}
return fArray;
}
7 Comments
Supplier<T> produces a value of type T without taking any input. It would be fitting in this question if the getX() methods were static.Awhile ago I used java.util.concurrent.Callable but it doesn't seem to work out, thanks to @Eran.
Instead, you can use Java 8's java.util.function.Function, like so (without the lambdas):
public static void main(String[] args) {
//...
getValuesAsArray(dataMap, new Function<DataStore,Float>(){ public Float apply(DataStore input) { return input.getA(); }});
getValuesAsArray(dataMap, new Function<DataStore,Float>(){ public Float apply(DataStore input) { return input.getB(); }});
getValuesAsArray(dataMap, new Function<DataStore,Float>(){ public Float apply(DataStore input) { return input.getC(); }});
}
private static float[] getValuesAsArray(Map<Integer, DataStore> dataMap, Function<DataStore, Float> function) {
int i = 0;
int nMap = dataMap.size();
float[] fArray = new float[nMap];
for (Map.Entry<Integer, DataStore> entry : dataMap.entrySet()) {
DataStore ds = entry.getValue();
fArray[i] = function.apply(ds);
i++;
}
return fArray;
}
3 Comments
getA(),getB(),getC() methods are not static. They should be called for a specific instance of DataStore.Callable so that the method can be invoked at a later time.DataStore.getA() if getA() is static, regardless of whether or not you wrap it with a Callable.There is a workaround: Scala java apis.
I use Apache Spark and scala offers a series of Anonymous Functions (Function, Function2) which are available since Java 1.5, if I'm not mistaken (although I use it with Java 1.7).
Here is an answer talking about this. Because otherwise the "Function" class is available only from Java 1.8
Comments
MethodReference is a class for reflection purpose. Your code actually need a lambda-like object, which shall be a single method interface in Java 8.
Without Java 8 or reflection there is no way to directally meet your need though. But you can always pass some internal representation of the method to another calss, and to do so you have to write code to process this internal representation.