What's the nearest substitute for a function p

2018-12-31 17:07发布

I have a method that's about ten lines of code. I want to create more methods that do exactly the same thing, except for a small calculation that's going to change one line of code. This is a perfect application for passing in a function pointer to replace that one line, but Java doesn't have function pointers. What's my best alternative?

21条回答
孤独总比滥情好
2楼-- · 2018-12-31 17:50

You need to create an interface that provides the function(s) that you want to pass around. eg:

/**
 * A simple interface to wrap up a function of one argument.
 * 
 * @author rcreswick
 *
 */
public interface Function1<S, T> {

   /**
    * Evaluates this function on it's arguments.
    * 
    * @param a The first argument.
    * @return The result.
    */
   public S eval(T a);

}

Then, when you need to pass a function, you can implement that interface:

List<Integer> result = CollectionUtilities.map(list,
        new Function1<Integer, Integer>() {
           @Override
           public Integer eval(Integer a) {
              return a * a;
           }
        });

Finally, the map function uses the passed in Function1 as follows:

   public static <K,R,S,T> Map<K, R> zipWith(Function2<R,S,T> fn, 
         Map<K, S> m1, Map<K, T> m2, Map<K, R> results){
      Set<K> keySet = new HashSet<K>();
      keySet.addAll(m1.keySet());
      keySet.addAll(m2.keySet());

      results.clear();

      for (K key : keySet) {
         results.put(key, fn.eval(m1.get(key), m2.get(key)));
      }
      return results;
   }

You can often use Runnable instead of your own interface if you don't need to pass in parameters, or you can use various other techniques to make the param count less "fixed" but it's usually a trade-off with type safety. (Or you can override the constructor for your function object to pass in the params that way.. there are lots of approaches, and some work better in certain circumstances.)

查看更多
唯独是你
3楼-- · 2018-12-31 17:51

If you have just one line which is different you could add a parameter such as a flag and a if(flag) statement which calls one line or the other.

查看更多
忆尘夕之涩
4楼-- · 2018-12-31 17:51

Since Java8, you can use lambdas, which also have libraries in the official SE 8 API.

Usage: You need to use a interface with only one abstract method. Make an instance of it (you may want to use the one java SE 8 already provided) like this:

Function<InputType, OutputType> functionname = (inputvariablename) {
... 
return outputinstance;
}

For more information checkout the documentation: https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

查看更多
伤终究还是伤i
5楼-- · 2018-12-31 17:52

You can also do this (which in some RARE occasions makes sense). The issue (and it is a big issue) is that you lose all the typesafety of using a class/interface and you have to deal with the case where the method does not exist.

It does have the "benefit" that you can ignore access restrictions and call private methods (not shown in the example, but you can call methods that the compiler would normally not let you call).

Again, it is a rare case that this makes sense, but on those occasions it is a nice tool to have.

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Main
{
    public static void main(final String[] argv)
        throws NoSuchMethodException,
               IllegalAccessException,
               IllegalArgumentException,
               InvocationTargetException
    {
        final String methodName;
        final Method method;
        final Main   main;

        main = new Main();

        if(argv.length == 0)
        {
            methodName = "foo";
        }
        else
        {
            methodName = "bar";
        }

        method = Main.class.getDeclaredMethod(methodName, int.class);

        main.car(method, 42);
    }

    private void foo(final int x)
    {
        System.out.println("foo: " + x);
    }

    private void bar(final int x)
    {
        System.out.println("bar: " + x);
    }

    private void car(final Method method,
                     final int    val)
        throws IllegalAccessException,
               IllegalArgumentException,
               InvocationTargetException
    {
        method.invoke(this, val);
    }
}
查看更多
裙下三千臣
6楼-- · 2018-12-31 17:55

For each "function pointer", I'd create a small functor class that implements your calculation. Define an interface that all the classes will implement, and pass instances of those objects into your larger function. This is a combination of the "command pattern", and "strategy pattern".

@sblundy's example is good.

查看更多
听够珍惜
7楼-- · 2018-12-31 17:55

The Google Guava libraries, which are becoming very popular, have a generic Function and Predicate object that they have worked into many parts of their API.

查看更多
登录 后发表回答