[java] Java에서 함수 포인터를 대체하는 가장 가까운 것은 무엇입니까?

약 10 줄의 코드가있는 방법이 있습니다. 한 줄의 코드를 변경하는 작은 계산을 제외하고는 정확히 동일한 작업을 수행하는 더 많은 메서드를 만들고 싶습니다. 이것은 한 줄을 대체하기 위해 함수 포인터를 전달하는 데 완벽한 응용 프로그램이지만 Java에는 함수 포인터가 없습니다. 최선의 대안은 무엇입니까?



답변

익명의 내부 클래스

String를 반환하는 매개 변수 와 함께 함수를 전달하려고한다고 가정 해보십시오 int.
먼저 기존 인터페이스를 재사용 할 수없는 경우 함수가있는 인터페이스를 유일한 멤버로 정의해야합니다.

interface StringFunction {
    int func(String param);
}

포인터를 취하는 메소드는 다음 StringFunction과 같이 인스턴스를 받아 들입니다.

public void takingMethod(StringFunction sf) {
   int i = sf.func("my string");
   // do whatever ...
}

그리고 이렇게 불릴 것입니다 :

ref.takingMethod(new StringFunction() {
    public int func(String param) {
        // body
    }
});

편집 : Java 8에서는 람다 식으로 호출 할 수 있습니다.

ref.takingMethod(param -> bodyExpression);


답변

각 “함수 포인터”에 대해 계산을 구현 하는 작은 functor 클래스 를 만듭니다 . 모든 클래스가 구현할 인터페이스를 정의하고 해당 객체의 인스턴스를 더 큰 함수로 전달하십시오. 이것은 ” 명령 패턴 “과 ” 전략 패턴 ” 의 조합입니다 .

@ sblundy의 예가 좋습니다.


답변

한 줄에서 미리 정의 된 수의 서로 다른 계산이있을 경우 열거 형을 사용하면 전략 패턴을 빠르고 명확하게 구현할 수 있습니다.

public enum Operation {
    PLUS {
        public double calc(double a, double b) {
            return a + b;
        }
    },
    TIMES {
        public double calc(double a, double b) {
            return a * b;
        }
    }
     ...

     public abstract double calc(double a, double b);
}

분명히, 전략 방법 선언과 각 구현의 정확히 하나의 인스턴스는 모두 단일 클래스 / 파일에 정의됩니다.


답변

전달하려는 함수를 제공하는 인터페이스를 작성해야합니다. 예 :

/**
 * 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);

}

그런 다음 함수를 전달해야 할 때 해당 인터페이스를 구현할 수 있습니다.

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

마지막으로 맵 함수는 다음과 같이 Function1에 전달 된 것을 사용합니다.

   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;
   }

매개 변수를 전달할 필요가없는 경우 자체 인터페이스 대신 Runnable을 사용하거나 다양한 다른 기술을 사용하여 매개 변수 수를 “고정”으로 만들 수는 없지만 일반적으로 형식 안전과의 균형을 유지해야합니다. 또는 함수 객체의 생성자를 재정 의하여 매개 변수를 그런 식으로 전달할 수 있습니다. 많은 방법이 있으며 일부 상황에서는 더 잘 작동합니다.


답변

::연산자를 사용한 메소드 참조

메소드가 기능 인터페이스를 허용하는 메소드 인수에서 메소드 참조를 사용할 수 있습니다 . 기능적 인터페이스는 하나의 추상 메소드 만 포함하는 모든 인터페이스입니다. 기능 인터페이스에는 하나 이상의 기본 메소드 또는 정적 메소드가 포함될 수 있습니다.

IntBinaryOperator기능적인 인터페이스입니다. 그것의 추상 메소드 는 매개 변수로 applyAsInt두 개의을 허용 int하고를 반환합니다 int. Math.max또한 두 개의을 허용하고을 int반환합니다 int. 이 예에서, A.method(Math::max);parameter.applyAsInt에 두 개의 입력 값을 전송 Math.max하고 그 결과를 리턴 Math.max.

import java.util.function.IntBinaryOperator;

class A {
    static void method(IntBinaryOperator parameter) {
        int i = parameter.applyAsInt(7315, 89163);
        System.out.println(i);
    }
}
import java.lang.Math;

class B {
    public static void main(String[] args) {
        A.method(Math::max);
    }
}

일반적으로 다음을 사용할 수 있습니다.

method1(Class1::method2);

대신에:

method1((arg1, arg2) -> Class1.method2(arg1, arg2));

이것은 짧다 :

method1(new Interface1() {
    int method1(int arg1, int arg2) {
        return Class1.method2(arg1, agr2);
    }
});

자세한 정보 는 Java 8Java 언어 사양 §15.13의 :: (이중 콜론) 연산자를 참조하십시오 .


답변

이 작업을 수행 할 수도 있습니다 (일부 RARE 경우에 적합합니다). 문제는 큰 문제입니다. 클래스 / 인터페이스 사용의 모든 유형 안전성을 잃어 버리고 메서드가 존재하지 않는 경우를 처리해야한다는 것입니다.

액세스 제한을 무시하고 개인 메소드를 호출 할 수있는 “혜택”이 있습니다 (예에서는 표시되지 않지만 컴파일러가 일반적으로 호출 할 수없는 메소드를 호출 할 수 있음).

다시 말하지만, 이것이 의미가있는 경우는 드물지만 그러한 경우에는 좋은 도구입니다.

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);
    }
}


답변

한 줄만 다른 경우 플래그와 같은 매개 변수를 추가하고 한 줄을 호출하는 if (flag) 문을 추가 할 수 있습니다.