문제

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

도움이 되었습니까?

해결책

익명의 내부 수업

기능을 전달하고 싶다고 말합니다. String 반환하는 매개 변수 int.
먼저 기존의 재사용을 재사용 할 수없는 경우 함수의 유일한 멤버로서 인터페이스를 정의해야합니다.

interface StringFunction {
    int func(String param);
}

포인터를 취하는 방법은 그냥 받아 들일 것입니다 StringFunction SO와 같은 인스턴스 :

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에서는 Lambda 표현으로 호출 할 수 있습니다.

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

매개 변수를 전달할 필요가없는 경우 자신의 인터페이스 대신 런 가능을 사용하거나 다양한 기타 기술을 사용하여 Param Count를 "고정"할 수 있지만 일반적으로 유형 안전과 상충됩니다. (또는 당신은 당신의 기능 개체의 생성자를 그런 식으로 전달할 수 있도록 만들 수 있습니다. 많은 접근 방식이 있으며 일부 상황에서는 더 잘 작동합니다.)

메소드 참조를 사용합니다 :: 운영자

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

IntBinaryOperator 기능 인터페이스입니다. 추상적 인 방법, applyAsInt, 두 가지를 받아들입니다 ints 매개 변수로서 반환합니다 int. Math.max 또한 두 가지를 받아들입니다 ints와 반환 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 8의 연산자 그리고 Java 언어 사양 §15.13.

당신은 또한 이것을 할 수 있습니다 (어떤 경우에는 희귀한 경우가 의미가 있습니다). 문제는 클래스/인터페이스를 사용하는 모든 유형 안전성을 잃고 메소드가 존재하지 않는 경우를 처리해야한다는 것입니다.

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

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

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) 문을 추가 할 수 있습니다.

폐쇄와 관련된 Java 7에서 진행되는 작업에 대해 듣고 싶을 수도 있습니다.

Java의 현재 폐쇄 상태는 무엇입니까?

http://gafter.blogspot.com/2006/08/closures-for-java.html
http://tech.puredanger.com/java7/#closures

새로운 Java 8 기능적 인터페이스 그리고 방법 참조 사용 :: 운영자.

Java 8은 메소드 참조 (myclass :: new)를 유지할 수 있습니다.@ 기능 인터페이스"포인터. 동일한 메소드 이름이 필요하지 않으며 동일한 메소드 서명 만 필요합니다.

예시:

@FunctionalInterface
interface CallbackHandler{
    public void onClick();
}

public class MyClass{
    public void doClick1(){System.out.println("doClick1");;}
    public void doClick2(){System.out.println("doClick2");}
    public CallbackHandler mClickListener = this::doClick;

    public static void main(String[] args) {
        MyClass myObjectInstance = new MyClass();
        CallbackHandler pointer = myObjectInstance::doClick1;
        Runnable pointer2 = myObjectInstance::doClick2;
        pointer.onClick();
        pointer2.run();
    }
}

그래서, 우리가 여기에 무엇을 가지고 있습니까?

  1. 기능 인터페이스 - 이것은 인터페이스, 주석이 달리거나 @functionalInterface, 하나의 메소드 선언 만 포함합니다.
  2. 메소드 참조 - 이것은 단지 특별한 구문 일뿐입니다. ObjectInstance :: MethodName, 더 이상 아무것도 없습니다.
  3. 사용 예제 - 할당 연산자와 인터페이스 메소드 호출.

청취자에게만 기능 인터페이스를 사용해야합니다.

다른 모든 기능 포인터는 코드 가독성과 이해 능력에 실제로 나쁘기 때문입니다. 그러나 직접 방법 참조는 때때로 Foreach와 함께 편리합니다.

사전 정의 된 기능 인터페이스가 몇 가지 있습니다.

Runnable              -> void run( );
Supplier<T>           -> T get( );
Consumer<T>           -> void accept(T);
Predicate<T>          -> boolean test(T);
UnaryOperator<T>      -> T apply(T);
BinaryOperator<T,U,R> -> R apply(T, U);
Function<T,R>         -> R apply(T);
BiFunction<T,U,R>     -> R apply(T, U);
//... and some more of it ...
Callable<V>           -> V call() throws Exception;
Readable              -> int read(CharBuffer) throws IOException;
AutoCloseable         -> void close() throws Exception;
Iterable<T>           -> Iterator<T> iterator();
Comparable<T>         -> int compareTo(T);
Comparator<T>         -> int compare(T,T);

이전 Java 버전의 경우 Adrian Petrescu가 위에서 언급 한 것처럼 비슷한 기능을 갖는 Guava 라이브러리와 구문을 사용해야합니다.

추가 연구를 보려면 Java 8 치트 시트

그리고 모자를 쓴 사람에게 감사합니다 Java 언어 사양 §15.13 링크.

@sblundy의 대답은 훌륭하지만 익명의 내부 클래스는 두 가지 작은 결함을 가지고 있으며, 기본은 재사용 할 수없는 경향이 있고 2 차는 부피가 큰 구문입니다.

좋은 점은 그의 패턴이 메인 클래스의 변화없이 전체 클래스로 확장된다는 것입니다 (계산을 수행하는 사람).

새 클래스를 인스턴스화하면 매개 변수를 해당 클래스로 전달할 수 있으며,이 클래스 중 하나가 다음과 같이 보이는 경우 다음과 같습니다.

f(x,y)=x*y

그러나 때로는 다음과 같은 것이 필요합니다.

f(x,y)=x*y*2

그리고 아마도 세 번째는 다음과 같습니다.

f(x,y)=x*y/2

두 개의 익명의 내부 클래스를 만들거나 "통과"매개 변수를 추가하는 대신 다음과 같이 인스턴스화하는 단일 실제 클래스를 만들 수 있습니다.

InnerFunc f=new InnerFunc(1.0);// for the first
calculateUsing(f);
f=new InnerFunc(2.0);// for the second
calculateUsing(f);
f=new InnerFunc(0.5);// for the third
calculateUsing(f);

단순히 클래스에 상수를 저장하고 인터페이스에 지정된 방법에 사용합니다.

실제로 기능이 저장/재사용되지 않는다는 것을 알고 있다면 다음을 수행 할 수 있습니다.

InnerFunc f=new InnerFunc(1.0);// for the first
calculateUsing(f);
f.setConstant(2.0);
calculateUsing(f);
f.setConstant(0.5);
calculateUsing(f);

그러나 불변의 수업은 더 안전합니다. 저는 이와 같은 수업을 만들기 위해 정당화를 내놓을 수 없습니다.

익명의 내부 수업을들을 때마다 울기 때문에 나는 이것을 실제로 게시합니다. 프로그래머가 처음으로 한 첫 번째 일은 실제 클래스를 사용했을 때 익명이었고 결코 필요했기 때문에 "필요한"중복 코드를 많이 보았습니다. 그의 결정을 다시 생각했습니다.

구글 구아바 도서관, 매우 인기를 얻고있는 일반적인 기능 그리고 술부 그들이 API의 많은 부분으로 일한 대상.

나에게 전략 패턴처럼 들린다. fluffycat.com Java 패턴을 확인하십시오.

Java에서 프로그래밍 할 때 내가 실제로 놓친 것 중 하나는 기능 콜백입니다. 이들 자체가 계속 제시 해야하는 한 가지 상황은 각 항목에 대해 특정 조치를 수행하려는 계층 구조를 재귀 적으로 처리하는 것이 었습니다. 디렉토리 트리를 걷거나 데이터 구조를 처리하는 것과 같습니다. 내부의 미니멀리스트는 인터페이스를 정의한 다음 각 특정 사례에 대한 구현을 정의해야합니다.

어느 날 나는 왜 그렇지 않은지 궁금해하는 것을 발견했습니다. 메소드 포인터 - 메소드 객체가 있습니다. JIT 컴파일러를 최적화하면 반사 호출은 더 이상 큰 성능 페널티를받지 않습니다. 예를 들어, 한 위치에서 다른 위치로 파일을 복사하는 것 외에도 반사 된 메소드 호출의 비용은 무의미합니다.

더 많이 생각했듯이 OOP 패러다임의 콜백은 객체와 메소드를 함께 바인딩해야한다는 것을 깨달았습니다. 콜백 객체를 입력하십시오.

반사 기반 솔루션을 확인하십시오 Java의 콜백. 모든 용도로 무료.

좋아,이 스레드는 이미 충분히 늙었으므로 아마도 내 대답은 질문에 도움이되지 않습니다. 그러나이 스레드는 내 솔루션을 찾는 데 도움이되었으므로 어쨌든 여기에 넣을 것입니다.

알려진 입력 및 알려진 출력이있는 가변 정적 메소드를 사용해야했습니다 (둘 다 더블). 따라서 메소드 패키지와 이름을 알면 다음과 같이 작업 할 수 있습니다.

java.lang.reflect.Method Function = Class.forName(String classPath).getMethod(String method, Class[] params);

하나를 매개 변수로 받아들이는 함수의 경우.

그래서 내 구체적인 상황에서 나는 그것을 초기화했다

java.lang.reflect.Method Function = Class.forName("be.qan.NN.ActivationFunctions").getMethod("sigmoid", double.class);

그리고 나중에 더 복잡한 상황에서 그것을 호출했습니다.

return (java.lang.Double)this.Function.invoke(null, args);

java.lang.Object[] args = new java.lang.Object[] {activity};
someOtherFunction() + 234 + (java.lang.Double)Function.invoke(null, args);

여기서 활동은 임의의 이중 값입니다. SoftWaremonkey가 한 것처럼이 작업을 조금 더 추상적으로하고 일반화 할 생각이지만 현재는 그 방식에 충분히 행복합니다. 세 줄의 코드, 클래스 및 인터페이스가 필요하지 않으며 그리 나쁘지 않습니다.

함수 배열에 대한 인터페이스없이 동일한 작업을 수행하려면 :

class NameFuncPair
{
    public String name;                // name each func
    void   f(String x) {}              // stub gets overridden
    public NameFuncPair(String myName) { this.name = myName; }
}

public class ArrayOfFunctions
{
    public static void main(String[] args)
    {
        final A a = new A();
        final B b = new B();

        NameFuncPair[] fArray = new NameFuncPair[]
        {
            new NameFuncPair("A") { @Override void f(String x) { a.g(x); } },
            new NameFuncPair("B") { @Override void f(String x) { b.h(x); } },
        };

        // Go through the whole func list and run the func named "B"
        for (NameFuncPair fInstance : fArray)
        {
            if (fInstance.name.equals("B"))
            {
                fInstance.f(fInstance.name + "(some args)");
            }
        }
    }
}

class A { void g(String args) { System.out.println(args); } }
class B { void h(String args) { System.out.println(args); } }

Lambdaj를 확인하십시오

http://code.google.com/p/lambdaj/

특히 새로운 폐쇄 기능

http://code.google.com/p/lambdaj/wiki/closures

그리고 무의미한 인터페이스를 만들거나 못생긴 내부 클래스를 사용하지 않고 클로저 또는 기능 포인터를 정의하는 매우 읽기 쉬운 방법을 찾을 수 있습니다.

와우, 내가 이미 Java를 위해했던 것을 감안할 때 그다지 어려운 대의원 클래스를 만들고 t가 반환 유형 인 매개 변수를 전달하는 데 사용하는 것이 어렵지 않습니까? 미안하지만 일반적으로 Java를 배우는 C ++/C# 프로그래머로서 기능 포인터가 매우 편리하기 때문에 기능 포인터가 필요합니다. 메소드 정보를 다루는 클래스에 익숙한 경우 수행 할 수 있습니다. java.lang.reflect.method 인 Java 라이브러리에서.

항상 인터페이스를 사용하는 경우 항상 인터페이스를 구현해야합니다. 이벤트를 처리 할 때는 핸들러 목록에서 등록/등록하는 데 더 나은 방법이 없지만 값 유형이 아닌 함수를 통과 해야하는 대표자에게는 대의원 클래스를 만들어 인터페이스를 아웃 클래스로 처리합니다.

Java 8의 답변 중 어느 것도 완전하고 응집력있는 예를 제공하지 않았으므로 여기에 있습니다.

다음과 같이 "함수 포인터"를 받아들이는 메소드를 선언합니다.

void doCalculation(Function<Integer, String> calculation, int parameter) {
    final String result = calculation.apply(parameter);
}

Lambda 표현을 제공하여 기능을 부르십시오.

doCalculation((i) -> i.toString(), 2);

누군가가 동작을 정의하기 위해 하나의 매개 변수 세트를 사용하는 함수를 전달하는 데 어려움을 겪고있는 경우, Scheme과 같이 실행할 다른 매개 변수 세트.

(define (function scalar1 scalar2)
  (lambda (x) (* x scalar1 scalar2)))

보다 Java에서 매개 변수 정의 동작으로 기능을 전달합니다

Java8이므로 공식 SE 8 API에 라이브러리가있는 Lambdas를 사용할 수 있습니다.

용법:하나의 추상 방법 만 사용하면 인터페이스를 사용해야합니다. 다음과 같이 인스턴스 (이미 제공된 Java SE 8을 사용하려면)를 만드십시오.

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

자세한 내용은 문서를 확인하십시오. https://docs.oracle.com/javase/tutorial/java/javaoo/lambdaexpressions.html

Java 8 이전에 가장 가까운 기능-포인터와 같은 기능을 대체하는 것은 익명의 클래스였습니다. 예를 들어:

Collections.sort(list, new Comparator<CustomClass>(){
    public int compare(CustomClass a, CustomClass b)
    {
        // Logic to compare objects of class CustomClass which returns int as per contract.
    }
});

그러나 이제 Java 8에서는 다음과 같은 매우 깔끔한 대안이 있습니다. 람다 표현, 다음과 같이 사용할 수 있습니다.

list.sort((a, b) ->  { a.isBiggerThan(b) } );

isbiggerthan은 방법입니다 CustomClass. 여기에서 메소드 참조를 사용할 수도 있습니다.

list.sort(MyClass::isBiggerThan);
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top