Примеры методов обратного вызова (callbacks)

Callback  в чистом виде, это когда вы сформировали задачу и отправили её выполнять какому то там процессу, но не ждёте её завершения и продолжаете  работать, а чтобы к вам мог вернутся результат работы, вы для воркера передаёте ссылку на свой класс с кастом в интерфейс калбека в котором обьявлен метод, при помощи которого воркер может вернуть данные. Вся работа происходит асинхронно без блокировок и ожиданий. В этом смысл калбеков, так как некоторые задачи могут идти долго, а некоторые быстро, но каллер не должен об этом заботиться, он просто предоставляет интерфейс для ответа и продолжает работу.

import java.util.ArrayList;
import java.util.Random;
 
/**
 *
 * @author mutagen
 */
public class CallBackDemo {
 
    public static void main(String[] args) {
 
        Caller caller = new Caller();
 
        for (int i = 0; i < 10; i++) {
            new Worker(caller).start();
        }
//        итого у нас 9 воркеров отработали и вернули ответ по калбеку в коллекцию
        System.out.println(caller.getStatuses());
    }
 
    static class Caller implements Callback {
 
        private ArrayList<Integer> statuses = new ArrayList<>();
 
        public ArrayList<Integer> getStatuses() {
            return statuses;
        }
 
        @Override
        public void callMeBack(int status) {
//            тут может быть логика куда вернутся данные, может быть коллекция, я выбрал попроще
            synchronized (statuses) {
                statuses.add(status);
            }
        }
    }
 
    static interface Callback {
//        подготовим интерфейс по которому нам будут возвращать данные
 
        void callMeBack(int status);
    }
 
    static class Worker extends Thread {
 
        private Callback cb;
 
        public Worker(Callback cb) {
            this.cb = cb;
        }
 
        int pleaseDoMeAFavor() {
            return new Random().nextInt();
        }
 
        @Override
        public void run() {
//            выполним работу 
            int st = pleaseDoMeAFavor();
//            и вернём данные вызывающему по калбек интерфейсу
            cb.callMeBack(st);
        }
    }
}

Допустим, мы разрабатываем фреймворк. У нас есть определенный шаблон действий, в котором только один пункт мы не можем прописать жестко и в этом месте необходимо, чтобы пользователь вставил свой функционал. Это можно сделать абстрактным методом, но в этом случае проблема: во-первых мы не сможем динамически менять такие “вставки”, во-вторых, для 1000 функционалов мы получим огромную иерархию наследования. Кроме того, так как множественное наследование в Java не поддерживается, наследование можно использовать только в отдельных случаях. В этом случае можно использовать коллбэки.
Яркий пример использования колбэков – Spring-овские темплейты для работы с JDBC – они исполняют общие функции самостоятельно, передавая пользователю только такие операции, как обработка ResultSet-ов.

У нас есть темплейтный класс:

package callbacks;
 
import java.util.Arrays;
import java.util.List;
 
public class Template {
 
	private ICustomOperation callback;
 
	public Template(ICustomOperation callback) {
		this.callback = callback;
	}
 
	public void setCallback(ICustomOperation callback) {
		this.callback = callback;
	}
 
	public void processData(List<String> lines) {
		for (String line : lines) {
			System.out.println("Input: " + line);
			line = "[" + line + "]";
			System.out.println("Standart operation 1 result : " + line);
 
			line = line.replaceAll(" ", "_");
			System.out.println("Standart operation 2 result : " + line);
 
			line = callback.evaluate(line); // здесь мы будем передавать контроль над обработкой пользователю
			System.out.println("Custom operation result     : " + line);
 
			line = line.toLowerCase();
			System.out.println("Standart operation 3 result : " + line);
		}
	}
 
}

Есть колбэк-интерфейс:

package callbacks;
 
public interface ICustomOperation {
 
	public String evaluate(String input);
 
}

И различные его реализации:

package callbacks;
 
public class CustomOperation implements ICustomOperation {
 
	public String evaluate(String input) {
		return "CUSTOM: " + input;
	}
 
}
package callbacks;
 
public class AnotherCustomOperation implements ICustomOperation {
 
	public String evaluate(String input) {
		return "ANOTHER CUSTOM: " + input;
	}
 
}

Теперь запуск:

Template template = new Template(new CustomOperation());
		List<String> data = Arrays.asList(new String[] { "first line", "SEcOnD LiNe" });
		template.processData(data);
 
		template.setCallback(new AnotherCustomOperation());
		template.processData(data);

Есть ещё одно применение: допустим, у нас есть объект, у которого private метод, который вы не хотите делать public или protected. Вы вызваете метод другого класса и изнутри него хотите сделать вызов нашего private-метода.Это можно сделать следующим образом:
package callbacks;

package callbacks;
 
public class PrivateFieldHolder {
 
	private OuterCaller outerCaller;
 
	public OuterCaller getOuterCaller() {
		return outerCaller;
	}
 
	public void setOuterCaller(OuterCaller outerCaller) {
		this.outerCaller = outerCaller;
	}
 
	public void publicMethod() {
		System.out.println("Holder: public method call");
 
		// здесь хитрая штука: мы передаем в вызов метода 
		// внешнего класса коллбэк, который
		// является анонимным классом внутри данного класса
		// т.к. этот анонимный класс видит private-метод, то мы
		// можем поместить внутрь callPrivateMethod вызов этого
		// private-метода. Таким образом при вызове 
		// callPrivateMethod во внешнем классе позволит обратиться
		// к private-методу этого класса.
		outerCaller.callFromOutsidePrivateMethod(new IPrivateMethodCallback() {
 
			public void callPrivateMethod(String caller) {
				privateMethod(caller);
			}
		});
 
	}
 
	private void privateMethod(String caller) {
		System.out.println(caller + ": private method call");
	}
 
}

Колбэк-интерфейс:

package callbacks;
 
public interface IPrivateMethodCallback {
 
	public void callPrivateMethod(String caller);
 
}

Внешний класс, которому нужно вызвать private-метод:

package callbacks;
 
public class OuterCaller {
 
	public void callFromOutsidePrivateMethod(IPrivateMethodCallback callback) {
		System.out.println("Outer method call");
		callback.callPrivateMethod("Outer caller");
	}
 
}

Теперь запуск:

PrivateFieldHolder pfh = new PrivateFieldHolder();
		OuterCaller oc = new OuterCaller();
		pfh.setOuterCaller(oc);
		pfh.publicMethod();

<Предыдущая        Оглавление      Следующая>