Java编程之委托代理回调、内部类以及匿名内部类回调(闭包回调)

最近一直在看Java的相关东西,因为我们在iOS开发是,无论是Objective-C还是Swift中,经常会用到委托代理回调,以及Block回调或者说是闭包回调。接下来我们就来看看Java语言中是如何实现委托代理回调以及闭包回调的。当然这两个技术点虽然实现起来并不困难,但是,这回调在封装一些公用组件时还是特别有用的。所以今天,还是有必要把Java中的委托代理回调以及闭包回调来单独的拿出来聊一下。

本篇博客我们依然依托于实例,先聊聊委托代理回调的实现和使用场景,然后再聊一下使用匿名内部类来进行回调,其实就是我们常说的“闭包”回调。闭包回调的实现方式其实就是匿名内部类的使用。既然本篇博客我们使用到了匿名内部类,我们就再聊一下Java中的内部类的相关东西。

一、委托代理回调

在iOS开发中,我们经常使用到委托代理回调,想TableView、CollectionView等等,这些高级控件会依赖于委托回调来完成一些配置。当然在Java中委托代理回调也是非常有用的,接下来我们就来看一下Java中的委托代理回调。当然在Swift或者OC中的委托代理回调是依托于“协议”的,Swift或者OC中的“协议”其实就是Java语言中的“接口”。所以在Java中的委托代理回调,依然要依托于“接口”来实现。

1、类图

首先我们给出该部分实例的类图,然后我们根据下方的类图来设计实现我们的具体代码。下方就是本部分所设计Demo的类图,当然,从类图中我们也能直观的看到,该示例是比较简单的,一共也就是一个接口两个类。CustomDelegate这个接口是代理类要实现的接口,其中包含了代理类要实现的方法。

从下方的类图中我们可以看出,代理类FirstClass实现了CustomDelegate代理接口,并实现了相关的代理方法。而SecondClass依赖于CustomDelegate接口,也就是说只要是实现了CustomDelegate接口的类都可以作为SecondClass的代理。而FirstClass中含有SecondClass类型的属性,并且FirstClass又实现了CustomDelegate接口,在FirstClass中,我们将secondClass对象的代理类指定为FirstClass,稍后我们在具体实现时将会介绍到。

2、代码的具体实现

根据上述类图,我们很容易的就可以给出相应的代码实现。接下来我们就根据上述类图来给出具体的代码实现。

(1)、CustomDelegate的代码实现

下方代码段就是CustomDelegate的具体实现。当然该接口的实现比较简单,就一个setValue(String value)方法。该方法的具体作用是用来相应参数回调的。下方我们会用到该方法。

package com.zeluli.callback.delegate;

public interface CustomDelegate {
    public void setValue(String value);
}

(2)、SecondClass的代码实现

CustomDelegate实现完毕后,接下来我们就来实现一下SecondClass的具体代码。下方代码段就是SecondClass的具体代码实现了。我们从具体实现中可以明确看出,SecondClass类中有个私有的delegate属性,该属性是CustomDelegate类型的,所以SecondClass依赖于CustomDelegate类型。

在SecondClass的构造方法中,我们为delegate指定了具体的对象,然后调用了begin()方法。begin()方法中做的事情也是比较简单的,就是使用了Java中自带的定时器,然后在特定时间的间隔中执行delegate对象的setValue()方法,并且将当前的时间传给setValue()方法。

package com.zeluli.callback.delegate;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class SecondClass {
    private CustomDelegate delegate;

    public SecondClass(CustomDelegate delegate) {
        this.delegate = delegate;
        this.begin();
    }
    
    public void begin() {
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                delegate.setValue(getNowDate());    //执行委托代理回调方法
            }
        };
        
        long delay = 0;  
        Timer timer = new Timer();  
        timer.scheduleAtFixedRate(task, delay, 1000); 
    }
    
    private String getNowDate() {
       Date currentTime = new Date();
       SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
       String dateString = formatter.format(currentTime);
       return dateString;
    }
}

(3)、FirstClass的创建

接下来我们来创建委托代理类,也就是我们的FirstClass类。其中的代码也是比较简单的,FirstClass类实现了CustomDelegate的相关方法,然后为secondClass对象指定了代理对象就是当前类的对象。具体代码如下所示。

 1 package com.zeluli.callback.delegate;
 2 
 3 public class FirstClass implements CustomDelegate {
 4     private SecondClass secondClass;
 5     
 6     public void beginRunSecondDelegateMethod() {
 7         if(this.secondClass == null) {
 8             this.secondClass = new SecondClass(this);
 9         }
10     }
11 
12     //secondClass回调要执行的方法
13     @Override
14     public void setValue(String value) {
15         System.out.println("第二个类回调过来的值:" + value);
16     }
17 
18 }

3、测试用例和运行结果

接下来我们来看一下上述代码的测试用例和运行结果。下方代码段就是我们的测试用例,代码比较简单,就是实例化了一个FirstClass的类对象firstObj,然后调用相应的方法为其中的secondClass指定代理方法即可,具体如下所示。

package com.zeluli.callback.delegate;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        FirstClass firstObj = new FirstClass();
        firstObj.beginRunSecondDelegateMethod();
    }
}

下方就是上述代码的运行结果,我们可以看出定期会执行FirstClass中的setValue()方法。

二、闭包回调

上面我们实现了委托代理回调,接下来我们来对上述示例进行改造。将其改成匿名内部类的实现方式,也就是使用闭包的形式来实现回调。我们只需要讲FirstClass进行修改即可。将其委托代理回调修改成闭包回调的形式。下方代码段就是我们修改后的FirstClass类的源代码。

从下方的源代码可以看出,FirstClass并没有实现CustomDelegate接口。在为SecondClass的对象指定委托代理对象时,我们传入的是一个匿名内部类的对象,而这个对象的类型是CustomDelegate。这种用法,也是匿名内部类的使用方式之一。

修改后的代码的测试用例以及运行结果与之前第一部分的委托代理回调的方式一致,在此就不做过多赘述了。

三、内部类

既然,上述我们使用到了匿名内部类,那么接下来的这部分我们就来看看内部类的相关内容。内部类,顾名思义,就是定义在接口、类、方法等结构的内部的类。而匿名内部类,就是没有名字的内部类,这一点也是比较好理解的。下方我们分别从迭代器的示例以及工厂模式的示例中来窥探一下内部类的具体使用场景及使用规则。当然这两个示例所针对的内部类的角度不同。

1、迭代器中的内部类

在之前的博客中,我们详细的聊了迭代器模式,详细内容请移步于《设计模式(十):从电影院中认识"迭代器模式"(Iterator Pattern)》。当然之前的迭代器我们是使用的Swift3.0来实现的,今天博客中我们就用Java的内部类来实现一个Java中的迭代器。

(1)、迭代器接口

按照之前的介绍迭代器的套路,我们还是先要创建迭代器接口的。下方的Selector就是我们创建的迭代器接口。

  • end()方法用来判断序列是否到达了结尾处。
  • current()方法则用来获取当前序列中下标的值。
  • next()方法则是移动下标到下一个位置。

为了统一迭代器使用规范性,所有的迭代器都要遵循该接口。具体代码如下所示。

(2)、创建序列类以及迭代器内部类

下方创建的就是我们的序列类Sequence,该类中的items数组用来存储元素,而next属性指向当前值的下标。在Sequence类中,除了属性、构造器以及方法外,我们还在其中定义了一个内部类SequenceSelector。

SequenceSelector类就是Sequence类的迭代器,并且SequenceSelector要实现迭代器接口Selector。下方我们要注意的一点,在内部类SequenceSelector中,可以直接访问外层类Sequence类的成员属性和方法。因为无论是内部类还是Sequence类的成员属性,都在Sequence类的域中。

当然下方的代码的逻辑是比较简单的,主要是对items数组的操作。具体代码如下所示。

(3)、上述迭代器的使用

定义完迭代器后,接下来,我们就来看一下迭代器的使用呢。首先我们创建一个序列对象,然后通过for循环往这个序列对象里边添加对象。紧接着我们从这个序列对象中获取其对应的迭代器对象,然后操作迭代器对序列进行遍历。具体操作如下所示。

2、工厂模式中的匿名内部类

聊完迭代器的内部类,接下来我们来看一下工厂模式中的匿名内部类。在之前的博客中,我们详细的聊了工厂模式的具体内容,详情请移步于《设计模式(四):从“兵工厂”中探索简单工厂、工厂方法和抽象工厂模式》。本篇博客我们就来看一下,匿名内部类在工厂模式中的使用。

(1)、类图

首先我们来看一下本部分所涉及案例的具体类图,下方就是我们当前要介绍内容的类图。

  • Service接口:首先我们来看一下Service接口,该接口是所有具体的实现类要实现的接口。其中定义这具体的方法声明。我们的实现类都要继承自该接口。
  • ServiceFactory接口:该接口是所有工厂类要实现的接口,因为本部分我们的工厂类是以匿名内部类的形式来体现的,所有该接口就是我们“匿名内部类”的类型。
  • Implemention1、2类:这两个类就是我们的具体实现类,我们的工厂就负责实例化这两个类。
  • Factories类:该类就负责调用工厂方法来创建相关实例,并执行实例的相关方法。

(2)、Service和ServiceFactory接口的具体实现

这两个接口的实现代码比较简单,在此就不做过多赘述了,具体代码如下所示:

 1 package com.zeluli.innerclass.factory;
 2 
 3 public interface Service {
 4     void method1();
 5     void method2();
 6 }
 7 
 8 ======================================================
 9 
10 package com.zeluli.innerclass.factory;
11 
12 public interface ServiceFactory {
13     Service getService();
14 }

(3)、Implementation相关类的实现

Implementation1和Implementation2的实现差不多,我们就聊一下Implementation1类的具体代码。从下方代码片段中我们可以看出Implementation1类实现了Service接口,并且给出了接口中相关方法的实现。并且在Implementation1类中有一个ServiceFactory类型的静态变量factory。而factory引用的是一个ServiceFactory类型的匿名内部类的对象。该匿名内部类就是一个工程类,其中有一个方法负责创建当前外围类,也就是Implementation1类的对象。具体实现如下所示。

(4)、Factory类的实现

接下来我们就来看看Factory类的实现,Factory中就负责从工厂中获取相应的对象,然后执行对象的相关方法,代码比较简单,就不做过多赘述了。

(5)、测试用例与运行结果

接下来我们来看一下上述实例的测试用例以及输出结果,如下所示:

今天的博客就先到这儿,下篇博客会继续聊Java的相关东西。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏hrscy

Swift2.1-下标脚本下标脚本

类,结构体和枚举可以定义下标脚本,下标脚本可以认为是访问集合(collection),列表或序列的成员元素。你可是使用下标脚本来设置或通过索引检索值,而不需要调...

8830
来自专栏架构之路

JavaScript的三种类型检测typeof , instanceof , toString比较

1.typeof typeof是js的一个操作符,在类型检测中,几乎没有任何用处。 typeof 返回一个表达式的数据类型的字符串,返回结果为javascrip...

35250
来自专栏zingpLiu

面向对象(三)【类的特殊成员及高级特性】

前面两篇文章介绍了类与对象的基本概念和类中的一些成员,本篇主要介绍类和对象的特殊成员及一些高级特性。

10430
来自专栏javathings

解释一下 HashMap 的工作原理

HashMap 是基于散列表的数据结构。所谓散列表,它通过键值对的方式存储数据,把 key 通过散列算法计算出一个存储地址,将 value 放入这个地址中。散列...

49810
来自专栏Python入门

python超详细的基础笔记你学会了么

python是一种面向对象的解释型计算机程序设计语言,python的是吉多·范罗苏姆(Guido van Rossum)于1989年发明

12220
来自专栏JavaEdge

(六)-class文件结构1 什么是JVM的“无关性”?2 纵观Class文件结构

31080
来自专栏琦小虾的Binary

map 学习(上)——C++中 map 的使用

map 学习(上)——C++中 map 的使用 欠下数据结构的债,迟早是要还的…… 最近写毕业论文过程中,需要用到哈希表的数据结构,此外空闲时间在刷 Leetc...

46360
来自专栏程序员的知识天地

python超详细的基础笔记,你还在苦恼怎么学吗?

python是一种面向对象的解释型计算机程序设计语言,python的是吉多·范罗苏姆(Guido van Rossum)于1989年发明

12430
来自专栏IT可乐

Redis详解(五)------ redis的五大数据类型实现原理

  前面两篇博客,第一篇介绍了五大数据类型的基本用法,第二篇介绍了Redis底层的六种数据结构。在Redis中,并没有直接使用这些数据结构来实现键值对数据库,而...

16900
来自专栏康怀帅的专栏

PHP 数据类型

PHP 支持三大类 8 种数据类型。 官方文档:http://php.net/manual/zh/language.types.php 标量(4) 布尔 boo...

31240

扫码关注云+社区

领取腾讯云代金券