programing tip

Java의 콜백 함수

itbloger 2020. 5. 30. 22:16
반응형

Java의 콜백 함수


Java 메소드에서 콜백 함수를 전달하는 방법이 있습니까?

내가 모방하려는 행동은 .Net Delegate가 함수에 전달되는 것입니다.

사람들이 별도의 객체를 만들 것을 제안하는 것을 보았지만 과잉으로 보이지만 때로는 과잉이 일을하는 유일한 방법이라는 것을 알고 있습니다.


.NET 익명 대리자와 같은 것을 의미한다면 Java의 익명 클래스도 사용할 수 있다고 생각합니다.

public class Main {

    public interface Visitor{
        int doJob(int a, int b);
    }


    public static void main(String[] args) {
        Visitor adder = new Visitor(){
            public int doJob(int a, int b) {
                return a + b;
            }
        };

        Visitor multiplier = new Visitor(){
            public int doJob(int a, int b) {
                return a*b;
            }
        };

        System.out.println(adder.doJob(10, 20));
        System.out.println(multiplier.doJob(10, 20));

    }
}

Java 8부터 람다 및 메소드 참조가 있습니다.

예를 들어 다음을 정의하십시오.

public class FirstClass {
    String prefix;
    public FirstClass(String prefix){
        this.prefix = prefix;
    }
    public String addPrefix(String suffix){
        return prefix +":"+suffix;
    }
}

import java.util.function.Function;

public class SecondClass {
    public String applyFunction(String name, Function<String,String> function){
        return function.apply(name);
    }
}

그럼 당신은 할 수 있습니다 :

FirstClass first = new FirstClass("first");
SecondClass second = new SecondClass();
System.out.println(second.applyFunction("second",first::addPrefix));

github에서 예제를 찾을 수 있습니다 : julien-diener / MethodReference .


간단히하기 위해 Runnable을 사용할 수 있습니다 .

private void runCallback(Runnable callback)
{
    // Run callback
    callback.run();
}

용법:

runCallback(new Runnable()
{
    @Override
    public void run()
    {
        // Running callback
    }
});

약간의 nitpicking :

사람들이 별도의 객체를 만들 것을 제안하는 것처럼 보였지만 과잉으로 보입니다.

콜백을 전달하는 것은 거의 모든 OO 언어로 별도의 객체를 생성하는 것을 포함하므로 과잉으로 간주 될 수 없습니다. 아마도 Java에서 별도의 클래스를 만들어야한다는 것입니다.이 클래스는 명시 적 일류 함수 또는 클로저가있는 언어보다 더 장황하고 자원 집약적입니다. 그러나 익명 클래스는 최소한 세부 정보를 줄이고 인라인으로 사용할 수 있습니다.


그러나 나는 그것이 기본적으로 이러한 답변에서 파생 무슨 내가 찾던 ..이었다 가장 선호하는 방법이 볼 수 있지만 나는 그것을 더 이상 중복 효율적으로 조작했다 ... 그리고 난 모든 사람들이 내가 가지고 올 것을 찾고 생각

요점 ::

먼저 간단한 인터페이스 만듭니다.

public interface myCallback {
    void onSuccess();
    void onError(String err);
}

결과를 처리하려고 할 때 마다이 콜백을 실행하십시오. 비동기 호출 후 더 가능성이 높으며이 reuslt에 의존하는 일부 항목을 실행하고 싶습니다.

// import the Interface class here

public class App {

    public static void main(String[] args) {
        // call your method
        doSomething("list your Params", new myCallback(){
            @Override
            public void onSuccess() {
                // no errors
                System.out.println("Done");
            }

            @Override
            public void onError(String err) {
                // error happen
                System.out.println(err);
            }
        });
    }

    private void doSomething(String param, // some params..
                             myCallback callback) {
        // now call onSuccess whenever you want if results are ready
        if(results_success)
            callback.onSuccess();
        else
            callback.onError(someError);
    }

}

doSomething 콜백을 추가하여 결과가 왔을 때 알리고 콜백 인터페이스를이 메소드에 매개 변수로 추가하는 데 시간이 걸리는 함수입니다.

내 요점이 분명하길 바랍니다.)


람다가있는 Java 8에서는 매우 쉽습니다.

public interface Callback {
    void callback();
}

public class Main {
    public static void main(String[] args) {
        methodThatExpectsACallback(() -> System.out.println("I am the callback."));
    }
    private static void methodThatExpectsACallback(Callback callback){
        System.out.println("I am the method.");
        callback.callback();
    }
}

리플렉션 라이브러리를 사용하여 구현하는 아이디어가 흥미 롭다는 것을 알았고 꽤 잘 작동한다고 생각했습니다. 유일한 단점은 유효한 매개 변수를 전달하고 있는지 컴파일 시간 검사를 잃는 것입니다.

public class CallBack {
    private String methodName;
    private Object scope;

    public CallBack(Object scope, String methodName) {
        this.methodName = methodName;
        this.scope = scope;
    }

    public Object invoke(Object... parameters) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        Method method = scope.getClass().getMethod(methodName, getParameterClasses(parameters));
        return method.invoke(scope, parameters);
    }

    private Class[] getParameterClasses(Object... parameters) {
        Class[] classes = new Class[parameters.length];
        for (int i=0; i < classes.length; i++) {
            classes[i] = parameters[i].getClass();
        }
        return classes;
    }
}

당신은 이것을 이렇게 사용합니다

public class CallBackTest {
    @Test
    public void testCallBack() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        TestClass testClass = new TestClass();
        CallBack callBack = new CallBack(testClass, "hello");
        callBack.invoke();
        callBack.invoke("Fred");
    }

    public class TestClass {
        public void hello() {
            System.out.println("Hello World");
        }

        public void hello(String name) {
            System.out.println("Hello " + name);
        }
    }
}

메소드는 (아직) Java에서 일류 객체가 아닙니다. 함수 포인터를 콜백으로 전달할 수 없습니다. 대신 필요한 메소드가 포함 된 오브젝트 (일반적으로 인터페이스를 구현 함)를 작성하여 전달하십시오.

찾고있는 동작을 제공하는 Java 클로저 제안이 작성되었지만 향후 Java 7 릴리스에는 포함되지 않습니다.


Java에서 이러한 종류의 기능이 필요할 때 일반적으로 Observer 패턴을 사용합니다 . 그것은 여분의 객체를 의미하지만, 나는 그것이 깔끔한 방법이라고 생각하고 널리 이해되는 패턴이며 코드 가독성에 도움이됩니다.


lambdaj 라이브러리에서 클로저가 어떻게 구현되었는지 확인하십시오. 실제로 C # 대리자와 매우 유사한 동작이 있습니다.

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


'콜백'을 구현하기 위해 java.lang.reflect를 사용해 보았습니다. 다음은 샘플입니다.

package StackOverflowQ443708_JavaCallBackTest;

import java.lang.reflect.*;
import java.util.concurrent.*;

class MyTimer
{
    ExecutorService EXE =
        //Executors.newCachedThreadPool ();
        Executors.newSingleThreadExecutor ();

    public static void PrintLine ()
    {
        System.out.println ("--------------------------------------------------------------------------------");
    }

    public void SetTimer (final int timeout, final Object obj, final String methodName, final Object... args)
    {
        SetTimer (timeout, obj, false, methodName, args);
    }
    public void SetTimer (final int timeout, final Object obj, final boolean isStatic, final String methodName, final Object... args)
    {
        Class<?>[] argTypes = null;
        if (args != null)
        {
            argTypes = new Class<?> [args.length];
            for (int i=0; i<args.length; i++)
            {
                argTypes[i] = args[i].getClass ();
            }
        }

        SetTimer (timeout, obj, isStatic, methodName, argTypes, args);
    }
    public void SetTimer (final int timeout, final Object obj, final String methodName, final Class<?>[] argTypes, final Object... args)
    {
        SetTimer (timeout, obj, false, methodName, argTypes, args);
    }
    public void SetTimer (final int timeout, final Object obj, final boolean isStatic, final String methodName, final Class<?>[] argTypes, final Object... args)
    {
        EXE.execute (
            new Runnable()
            {
                public void run ()
                {
                    Class<?> c;
                    Method method;
                    try
                    {
                        if (isStatic) c = (Class<?>)obj;
                        else c = obj.getClass ();

                        System.out.println ("Wait for " + timeout + " seconds to invoke " + c.getSimpleName () + "::[" + methodName + "]");
                        TimeUnit.SECONDS.sleep (timeout);
                        System.out.println ();
                        System.out.println ("invoking " + c.getSimpleName () + "::[" + methodName + "]...");
                        PrintLine ();
                        method = c.getDeclaredMethod (methodName, argTypes);
                        method.invoke (obj, args);
                    }
                    catch (Exception e)
                    {
                        e.printStackTrace();
                    }
                    finally
                    {
                        PrintLine ();
                    }
                }
            }
        );
    }
    public void ShutdownTimer ()
    {
        EXE.shutdown ();
    }
}

public class CallBackTest
{
    public void onUserTimeout ()
    {
        System.out.println ("onUserTimeout");
    }
    public void onTestEnd ()
    {
        System.out.println ("onTestEnd");
    }
    public void NullParameterTest (String sParam, int iParam)
    {
        System.out.println ("NullParameterTest: String parameter=" + sParam + ", int parameter=" + iParam);
    }
    public static void main (String[] args)
    {
        CallBackTest test = new CallBackTest ();
        MyTimer timer = new MyTimer ();

        timer.SetTimer ((int)(Math.random ()*10), test, "onUserTimeout");
        timer.SetTimer ((int)(Math.random ()*10), test, "onTestEnd");
        timer.SetTimer ((int)(Math.random ()*10), test, "A-Method-Which-Is-Not-Exists");    // java.lang.NoSuchMethodException

        timer.SetTimer ((int)(Math.random ()*10), System.out, "println", "this is an argument of System.out.println() which is called by timer");
        timer.SetTimer ((int)(Math.random ()*10), System.class, true, "currentTimeMillis");
        timer.SetTimer ((int)(Math.random ()*10), System.class, true, "currentTimeMillis", "Should-Not-Pass-Arguments");    // java.lang.NoSuchMethodException

        timer.SetTimer ((int)(Math.random ()*10), String.class, true, "format", "%d %X", 100, 200); // java.lang.NoSuchMethodException
        timer.SetTimer ((int)(Math.random ()*10), String.class, true, "format", "%d %X", new Object[]{100, 200});

        timer.SetTimer ((int)(Math.random ()*10), test, "NullParameterTest", new Class<?>[]{String.class, int.class}, null, 888);

        timer.ShutdownTimer ();
    }
}

패턴을 Callback사용하여 수행 할 수도 있습니다 Delegate.

콜백 .java

public interface Callback {
    void onItemSelected(int position);
}

PagerActivity.java

public class PagerActivity implements Callback {

    CustomPagerAdapter mPagerAdapter;

    public PagerActivity() {
        mPagerAdapter = new CustomPagerAdapter(this);
    }

    @Override
    public void onItemSelected(int position) {
        // Do something
        System.out.println("Item " + postion + " selected")
    }
}

CustomPagerAdapter.java

public class CustomPagerAdapter {
    private static final int DEFAULT_POSITION = 1;
    public CustomPagerAdapter(Callback callback) {
        callback.onItemSelected(DEFAULT_POSITION);
    }
}

최근에 다음과 같은 일을 시작했습니다.

public class Main {
    @FunctionalInterface
    public interface NotDotNetDelegate {
        int doSomething(int a, int b);
    }

    public static void main(String[] args) {
        // in java 8 (lambdas):
        System.out.println(functionThatTakesDelegate((a, b) -> {return a*b;} , 10, 20));

    }

    public static int functionThatTakesDelegate(NotDotNetDelegate del, int a, int b) {
        // ...
        return del.doSomething(a, b);
    }
}

그것은 조금 낡았지만 그럼에도 불구하고 ... Peter Wilkinson의 대답은 int / Integer와 같은 기본 유형에는 작동하지 않는다는 사실을 제외하고는 좋았습니다. 문제는에 .getClass()대한 것 parameters[i]입니다. 예를 들어 반환하는 java.lang.Integer반면에 getMethod(methodName,parameters[])(Java의 결함)에 의해 올바르게 해석되지 않습니다 ...

나는 (다니엘 스피 웍의 제안으로 결합 이 그의 대답 ) 성공의 단계는 다음과 같습니다. catching NoSuchMethodException-> getMethods()-> 일치하는 항목을 하나씩 찾고 method.getName()-> 매개 변수 목록을 명시 적으로 반복하고 Daniels 솔루션을 적용하여 유형 일치 및 서명 일치를 식별합니다.


다음과 같이 추상 클래스를 사용하는 것이 더 우아하다고 생각합니다.

// Something.java

public abstract class Something {   
    public abstract void test();        
    public void usingCallback() {
        System.out.println("This is before callback method");
        test();
        System.out.println("This is after callback method");
    }
}

// CallbackTest.java

public class CallbackTest extends Something {
    @Override
    public void test() {
        System.out.println("This is inside CallbackTest!");
    }

    public static void main(String[] args) {
        CallbackTest myTest = new CallbackTest();
        myTest.usingCallback();
    }    
}

/*
Output:
This is before callback method
This is inside CallbackTest!
This is after callback method
*/

public class HelloWorldAnonymousClasses {

    //this is an interface with only one method
    interface HelloWorld {
        public void printSomething(String something);
    }

    //this is a simple function called from main()
    public void sayHello() {

    //this is an object with interface reference followed by the definition of the interface itself

        new HelloWorld() {
            public void printSomething(String something) {
                System.out.println("Hello " + something);
            }
        }.printSomething("Abhi");

     //imagine this as an object which is calling the function'printSomething()"
    }

    public static void main(String... args) {
        HelloWorldAnonymousClasses myApp =
                new HelloWorldAnonymousClasses();
        myApp.sayHello();
    }
}
//Output is "Hello Abhi"

인터페이스의 객체를 만들 수 없기 때문에 기본적으로 인터페이스의 객체를 만들려면 불가능합니다.

옵션은 일부 클래스가 인터페이스를 구현 한 다음 해당 클래스의 객체를 사용하여 해당 함수를 호출하도록하는 것입니다. 그러나이 방법은 정말 장황합니다.

또는 new HelloWorld ()를 작성하고 (* 이것은 클래스가 아닌 인터페이스 임) 인터페이스 메소드 자체의 정의에 따라 수행하십시오. (*이 정의는 실제로 익명 클래스입니다). 그런 다음 메소드 자체를 호출 할 수있는 객체 참조를 얻습니다.


콜백 클래스에서 인터페이스를 만들고 동일한 인터페이스 속성을 만듭니다.

interface dataFetchDelegate {
    void didFetchdata(String data);
}
//callback class
public class BackendManager{
   public dataFetchDelegate Delegate;

   public void getData() {
       //Do something, Http calls/ Any other work
       Delegate.didFetchdata("this is callbackdata");
   }

}

이제 다시 호출하려는 클래스에서 위의 생성 된 인터페이스를 구현하십시오. 또한 클래스의 "this"객체 / 참조를 전달하여 다시 호출하십시오.

public class Main implements dataFetchDelegate
{       
    public static void main( String[] args )
    {
        new Main().getDatafromBackend();
    }

    public void getDatafromBackend() {
        BackendManager inc = new BackendManager();
        //Pass this object as reference.in this Scenario this is Main Object            
        inc.Delegate = this;
        //make call
        inc.getData();
    }

    //This method is called after task/Code Completion
    public void didFetchdata(String callbackData) {
        // TODO Auto-generated method stub
        System.out.println(callbackData);
    }
}

참고 URL : https://stackoverflow.com/questions/443708/callback-functions-in-java

반응형