Java 메소드에서 콜백 함수를 전달하는 방법이 있습니까?
내가 모방하려는 행동은 .Net Delegate가 함수에 전달되는 것입니다.
사람들이 별도의 객체를 만들 것을 제안하는 것을 보았지만 과잉으로 보이지만 때로는 과잉이 유일한 방법이라는 것을 알고 있습니다.
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부터 람다 및 메소드 참조가 있습니다.
예를 들어 다음 A -> B
과 같은 기능 인터페이스를 원할 경우
import java.util.function.Function;
public MyClass {
public static String applyFunction(String name, Function<String,String> function){
return function.apply(name);
}
}
그럼 그렇게 불러
MyClass.applyFunction("42", str -> "the answer is: " + str);
// returns "the answer is: 42"
또한 클래스 메소드를 전달할 수 있습니다. 당신이 가지고 있다고 :
@Value // lombok
public class PrefixAppender {
private String prefix;
public String addPrefix(String suffix){
return prefix +":"+suffix;
}
}
그럼 당신은 할 수 있습니다 :
PrefixAppender prefixAppender= new PrefixAppender("prefix");
MyClass.applyFunction("some text", prefixAppender::addPrefix);
// returns "prefix:some text"
참고 :
여기서는 기능 인터페이스 Function<A,B>
를 사용했지만 패키지에는 다른 인터페이스 가 많이 있습니다 java.util.function
. 가장 주목할만한 것은
Supplier
: void -> A
Consumer
: A -> void
BiConsumer
: (A,B) -> void
Function
: A -> B
BiFunction
: (A,B) -> C
입 / 출력 유형 중 일부를 전문으로하는 다른 많은 것. 그런 다음 필요한 기능을 제공하지 않으면 다음과 같이 자체 기능 인터페이스를 만들 수 있습니다.
@FunctionalInterface
interface Function3<In1, In2, In3, Out> { // (In1,In2,In3) -> Out
public Out apply(In1 in1, In2 in2, In3 in3);
}
사용 예 :
String computeAnswer(Function3<String, Integer, Integer, String> f){
return f.apply("6x9=", 6, 9);
}
computeAnswer((question, a, b) -> question + "42");
// "6*9=42"
java.util.function
가 찾고있는 것이라면 갈 수 있습니다. 그런 다음 I / O의 제네릭으로 재생할 수 있습니다. (?)
java.util.function
는 충분하지 않기 때문에 자체 기능 인터페이스를 작성해야한다는 것 입니다.
간단히하기 위해 Runnable을 사용할 수 있습니다 .
private void runCallback(Runnable callback)
{
// Run callback
callback.run();
}
용법:
runCallback(new Runnable()
{
@Override
public void run()
{
// Running callback
}
});
public interface SimpleCallback { void callback(Object... objects); }
이것은 매우 간단하며 일부 매개 변수를 전달해야 할 수도 있습니다.
조금 따끔 따끔 :
사람들이 별도의 객체를 만들 것을 제안하는 것처럼 보였지만 과잉으로 보입니다.
콜백을 전달하는 것은 거의 모든 OO 언어로 별도의 객체를 생성하는 것을 포함하므로 과잉으로 간주 될 수 없습니다. 아마도 자바에서 별도의 클래스를 만들어야한다는 것입니다.이 클래스는 명시 적 일류 함수 또는 클로저가있는 언어보다 더 장황하고 자원 집약적입니다. 그러나 익명 클래스는 최소한 세부 정보를 줄이고 인라인으로 사용할 수 있습니다.
그러나 나는 그것이 기본적으로 이러한 답변에서 파생 무슨 내가 찾던 ..이었다 가장 선호하는 방법이 볼 수 있지만 나는 그것을 더 이상 중복 효율적으로 조작했다 ... 그리고 난 모든 사람들이 내가 가지고 올 것을 찾고 생각
먼저 간단한 인터페이스 를 만듭니다.
public interface myCallback {
void onSuccess();
void onError(String err);
}
결과를 처리하려고 할 때 마다이 콜백을 실행하십시오. 비동기 호출 후 더 가능성이 높으며 이러한 재사용에 의존하는 일부 항목을 실행하고 싶습니다.
// 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 # 대리자와 매우 유사한 동작이 있습니다.
'콜백'을 구현하기 위해 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 ();
}
}
timer.SetTimer ((int)(Math.random ()*10), System.out, "printf", "%s: [%s]", new Object[]{"null test", null});
. 출력은null test: [null]
args[i].getClass()
아닌가요? 내 요점은 인수 유형에 따라 메소드를 선택하면 작동하지 않는다는 것입니다. 그것은 작동String.format
하지만 받아들이는 다른 것에서는 작동하지 않을 수 있습니다 null
.
argTypes
배열을 수동으로 전달할 수있는 함수를 추가 했으므로 이제 null
NullPointerException없이 인수 / 매개 변수를 전달할 수 있습니다 . 샘플 출력 :NullParameterTest: String parameter=null, int parameter=888
패턴을 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[])
(자바의 잘못) ...
나는 (다니엘 스피 웍의 제안으로 결합 이 그의 대답 ) 성공하는 단계는 다음과 같습니다. 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
*/
object
다른 사람 에게 "알림" 하도록 설계되었으므로 object
두 번째 object
는 이벤트를 처리 할 수 있습니다. 당신 abstract class
은 결코 별개 의 것이 아니며 object
단지 별개의 것 class
입니다. 콜백 패턴이 아닌 다형성의 본질 인 다른 기능을 수행하기 위해 하나만 사용 object
하고 classes
다른 기능을 사용하고 있습니다.
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);
}
}