Home Ask Login Register

Developers Planet

Your answer is one click away!

rbellamy February 2016

Java - how do I pass a method as parameter?

I have a stanza of code that is repeatedly used. Here are two examples:

public java.sql.Struct createStruct(String typeName, Object[] attributes) {
  String searchPath = getSearchPath();
  String name = setSearchPathToSchema(typeName);
  Struct ret = delegate().createStruct(name.toLowerCase(), attributes);
  setSearchPath(searchPath);
  return ret;
}

public java.sql.Array createArray(String typeName, Object[] elements) {
  String searchPath = getSearchPath();
  String name = setSearchPathToSchema(typeName);
  Array ret = delegate().createArray(name.toLowerCase(), elements);
  setSearchPath(searchPath);
  return ret;
}

You can see that these two methods have the general form:

public <T> createXXX(String typeName, Object[] objects) {
  String searchPath = getSearchPath();
  String name = setSearchPathToSchema(typeName);
  T ret = delegate().createXXX(name.toLowerCase(), objects);
  setSearchPath(searchPath);
  return ret;
}

Where T is the return type of some set of functions createXXX that have a common signature but different return type.

I'm pretty sure how I would do this in Javascript, F#, C#, Scala or any number of other languages.

I just can't seem to wrap my head around how to do this in Java in a type-safe manner that allows me to call each createXXX method so that the setup and tear-down can happen in one block of code, rather than sprinkled throughout each wrapper method.

I know I could use reflection, but am looking for a way to do this using lambdas, if possible, or whatever structure makes the most sense in Java.

Something like this, where the method to be called is passed as the third parameter (I know this code is completely malformed in Java):

public Struct createStruct(String typeName, Object[] attributes) {
  return createTypeWithCorrectSearchPath<>(
           typeName, 
                  

Answers


Louis Wasserman February 2016

interface MyMethodType<T> {
  T method(String name, Object[] objects);
}


private <T> T createTypeWithCorrectSearchPath(
            String typeName, 
            Object[] objects, 
            MyMethodType<T> impl) {
    String searchPath = getSearchPath();
    String name = setSearchPathToSchema(typeName);
    T ret = impl.method(name, objects);
    setSearchPath(searchPath);
    return ret;
}

createTypeWithCorrectSearchPath(typeName, objects, delegate()::createStruct);

The crucial bits are a) creating your own interface type, though I suppose you could use BiFunction<String, Object[]>, b) using method references with ::.


Neuron February 2016

You can solve this problem by passing objects which have the sole purpose of performing that one function. Here is a little example to you give you an idea. Create an interface for the type of function. You need to use generics as I did here:

public interface Creator<A>{
    A create(String name, Object[] attributes);
}

Then define your method which takes functions of the type you just specified:

public <A> A create(String typeName, Object[] attributes, Creator<A> creator){
    String searchPath = getSearchPath();
    String name = setSearchPathToSchema(typeName);
    A ret = creator.create(name.toLowerCase(), attributes);
    setSearchPath(searchPath);
    return ret;
}

The most convenient way are anonymous classes where you define the interfaces implementation in line:

java.sql.Struct struct = create("foo bar", new Object[]{"att1", "att2"}, new Creator<Struct>() {
    @Override
    public Struct create(String name, Object[] attributes) {
        return //your implementation..
    }
})

if you can't put the implementation into an anonymous class, because you need to access delegate().createXXX(...), simply put the definition of the class implementing the interface into the a scope where your method becomes accessible.


ajb February 2016

Louis' answer is on the right track; however, it's not clear to me that delegate() would be available at the point where the create method is called. If it isn't available, then you'll need a three-argument interface method.

I don't know what type delegate() returns, but suppose it's Delegate. One thing to note is that if Delegate has an instance method createArray(String s, Object[] objects), you can use Delegate::createArray as a method reference for a functional interface for a function with three arguments. The first argument would be a Delegate. Thus:

interface MyMethodType<T> {
    T method(Delegate delegate, String name, Object[] objects);
}

Now, in your createTypeWithCorrectSearchPath method, you would call the interface like this:

impl.method(delegate(), name.toLowerCase(), objects);

The first parameter of the call would become the instance on which the two-argument instance method operates. That is, if the actual parameter is Delegate::createArray, it would be called like

delegate().createArray(name.toLowerCase(), objects);

Unlike in Louis' answer, you have to define your own interface here, because there's no built-in TriFunction class in Java 8.

See Section 15.13.3 of the JLS for a complete description of how method references can be used. This particular one is listed in the paragraph starting "If the form is ReferenceType :: [TypeArguments] Identifier".

EDIT: After taking another look at the question, I see that createTypeWithCorrectSearchPath was intended to be a private method, and not called from the outside. So this answer probably isn

Post Status

Asked in February 2016
Viewed 3,304 times
Voted 14
Answered 3 times

Search




Leave an answer


Quote of the day: live life