Java函数回调

如何使用没有函数类型的Java语言,写出一个回调函数呢?

Java 8 的函数指针

接口重载

通过接口重载,Java中可以很容易地实现”多态“。


    public interface MyInterface {

        void greeting();

    }

    public class MyInterfaceImpl implements MyInterface {

        @Override
        void greeting() {
            System.out.println("你好");
        }

    }

    public static void main(String args[]) {
        MyInterface i = new MyInterfaceImpl();
        i.greeting();
    }

接口MyInterface无法被实例化,但是被允许以参数的形式传入方法中。在调用该方法时,调用方不得不去实现该接口内定义的方法。

Java中使用这种方法进行回调的例子很多,最典型的就是Thread这个对象。


    public static void main(String args[]){

        new Thread(new Runnable() {

            @Override
            public void run() {
                // your codes here
            }

        }).start(););

    }

当然,在Java程序规范中并不鼓励以上这种写法。面向对象的观点倾向于抽象出一个具体的实现类,然后调用这个具体的实现类。(除非是用完之后就被使用者抛弃)

基于此,我们可以很容易的写出这种类似回调函数的东西。


    interface GreetingByWeather {

        void greeting(Person person);

    }

    class Person {

        public String name;

        public Person () {};

        public Person (String name) {
            this.name = name;
        }

        public void greeting (GreetingByWeather greetingByWeather) {
            greetingByWeather.greeting(this);
        }

        public String say() {
            return "说:";
        }

    }

    public static void main(String args[]) {

        Person ming = new Person("小明");
        ming.greeting(new GreetingByWeather {

            @Override
            public void greeting(Person person) {
                System.out.println("今天是个大晴天!");
                System.out.print(person.name);
                System.out.print(person.say());
                System.out.println("你好!");
            }

        });

        ming.greeting(new GreetingByWeather {

            @Override
            public void greeting(Person person) {
                System.out.println("今天下起了雨!");
                System.out.print(person.name);
                System.out.println("没有出门。");
            }

        });

    }

    // 运行结果
    今天是个大晴天!
    小明说:你好!

    今天下去了雨!
    小明没有出门。

通过上面的例子,我们发现,接口GreetingByWeather定义了一个方法,传入了一个Person参数,返回了一个结果(void)。这也意味着,如果我们没有对接口进行声明,那么我们也无法正常地调用该函数(废话blabla……)

如果,能在业务调用的时候声明参数和返回值的类型,该有多好啊。

函数接口

Java 8 带来了许多新特性,诸如枚举类、lambda表达式、接口默认方法和函数接口等。其中,大部分都是基于原有的Java特性的增强和补充。而像lambda表达式之类的,更像是一种函数式编程的语法糖,它让原有的函数式编程代码更加简洁。

java.util.function包下,多了许多函数式的接口声明,它们也可以被近似地看做是一种语法糖。
而事实上,Java8为了在函数式编程的方向上有所发展,放弃了简单的接口重载,而是通过动态调用 **[invokeddynamic](https://stackoverflow.com/questions/30002380/why-are-java-8-lambdas-invoked-using-invokedynamic)** 来实现的。

下面以Function接口为例。


    @FunctionalInterface
    public interface Function<T, R> {

        /**
        * Applies this function to the given argument.
        *
        * @param t the function argument
        * @return the function result
        */
        R apply(T t);

    public class Person {

        ...

        public void greeting(Function<Person, Boolean> function) {
            boolean result = function.apply(this);
            // do sth with result ……
        }

        ...

    }

    public static void main(String args[]) {

        Person ming = new Person("小明");
        ming.greeting(new Function<Person, String>() {
            @Override
            public String apply(Person person) {
                System.out.println(person.name + person.say() + "你好");
                return true;
            }
        });
    }

上面的实现还可以轻易被lambda表达式替代。


    ming.greeting(person -> {
        System.out.println(person.name + "没有出门");
        return false;
    });

Function接口,允许我们在编写具体业务代码的同时,声明传入的参数和返回值类型。而我们也不必花费额外的开销去声明一个接口。这种书写方式看上去和接口重载很像,但绝不一样。

Java中的动态代理

前面我们已经提到过AOP编程。

而一提到AOP,就让人联想到Java中声名显赫的Spring框架。Spring带来了管家式的编程体验,几乎接管了J2EE世界里的一切组件。在某种程度上,我们讨论Java Web编程的时候,就是在讨论Spring框架。

而在这里,我们脱离Spring框架,使用原生的Java代码来实现动态代理。

定义接口和实现


    public interface GreetingService {

        void greeting(Person person);

    }

    public class GreetingServiceImpl implements GreetingService {

        @Override
        void greeting(Person person) {
            System.out.println(person.name + person.say() + "你好!");
        }

    }

    public class Person {

        ...

        public void greeting(greetingService greetingService) {
           greetingService.greeting(this);
        }

        ...

    }

自jdk1.3版本以来,反射机制被引入Java体制内,它可以获取到被编译完成的类中之属性、方法或注解等元素。

定义一个InvocationHandler的实现类


    private class GreetingIHImpl implements InvocationHandler {

        private Object obj;

        GreetingIHImpl(Object obj) {
            this.obj = obj;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
          throws Throwable {
            System.out.println("在 " + DateFormat.getDateTimeInstance().format(new Date()) + " 时:");
            Object reflectObj = method.invoke(obj, args);
            return reflectObj;
        }

    }

最后,我们在调用greetingService.greeting()方法的时候,将代理中的实现GreetingIHImpl织入到原有的接口中。


    GreetingService greetingService = (GreetingService) Proxy.newProxyInstance(
        GreetingService.class.getClassLoader(),
        new Class[]{GreetingService.class},
        new GreetingIHImpl(new GreetingServiceImpl())
    );

    ...

    ming.greeting(greetingService);

    // 输出
    在 2018-07-12 17:19:54 时:
    小明说: 你好!

posted @ 2021-08-16 17:10  风中凌猿  阅读(1072)  评论(0编辑  收藏  举报