Java内部类(2): 用法和回调

xiaoxiao2021-02-28  120

为什么需要内部类

一般来说,内部类继承自某个类或实现了某个接口,内部类代码操作创建它的外部类的对象,可以说,内部类提供了某种进入外部类的窗口,而且内部类自己本身实现了有效的代码隐藏。 内部类必须要回答的一个问题是:如果只是需要一个接口的引用,为什么外部类不直接实现接口呢。答案是:如果这能满足需求,就应该这么做!那么内部类实现一个接口于外部类实现这个接口有什么区别呢?答案是:后者并不总能享受接口带来的便利,有时候需要接口的实现。使用内部类最吸引人的地方在于: 外部类可以有多个内部类,每个内部类都能够独自实现一个接口(继承一个类),且无论外围类是否实现该接口(继承该类)都不会对内部类产生影响。

我们考虑这样一种情况,在一个类中需要以某种方式实现两个接口A和B。这两个接口相互独立,彼此直接没有任何联系。那么有两种方式:

直接实现两个接口该类实现接口A,另外构建一个内部类实现接口B 一般情况下两种方式都可以,但对于第二种方式来说,它把两个接口的实现完全分离,并且可以隐藏B接口实现的细节。 拿HashMap举例,就是通过内部类来实现Iterator接口的,既实现了hashmap的遍历功能,又保证了得到的遍历器能够访问原来的map对象并且对外部来说完全隐藏了遍历器的实现细节。如果现在需要hashmap实现另一个反向遍历接口ReIterator,那么只需要再定义一个内部类实现反向遍历接口,通过方法返回内部类实例就可以,并不需要修改外部类的实现接口。

另一种情况:如果好巧不巧的,A、B两个接口有一个相同的方法(虽然方法名相同,但完全独立,是两个互不关联的方法),那么第一种方式就不可行了,因为你只能在类中定义一个该方法,无法区分属于哪个接口。

另外一点:可以定义多个内部类,并且这些内部类相互独立。考虑这样一种情况,在一个类中需要以某种方式实现多个接口(5个以上并且每个接口的方法很多)那么,如果直接实现这些接口,那么类中的方法数量将会很多,类会变得很庞大,这也不符合代码分离的理念。 从某种程度上来说,内部类完善了java的多继承

在单个外部类中,可以让多个内部类以不同的方式实现同一个接口或继承同一个类。就像HashMap,我们可以以多种方式实现遍历操作。

内部类与回调

回调用于层间协作。例如一个驱动,是一个地层。但有数据传给它时,它不仅要处理自己本层的工作,还要进行回调,将数据传给它的上层供上层处理。 回调跟API有点相似,都是跨层级调用的。不过与API不同的是,API是下层提供给上层去调用的,那么上层主动去调用下层API,这叫Call,上层是知道所调用的下层方法的。但回调不同,回调是上层提供给下层去调用的,而且具体方法下层是不知道的,下层只知道自己需要在某些条件下调用这个方法。上层向下层注入自己的程序(比如一个对象),然后下层通过这段程序(这个对象)来反过来调用上层的方法,这叫回调。

而Java的回调呢,就是通过接口和内部类来实现的。通常是别人提供一个接口,我用内部类实现这个接口并将内部类的一个实例注入到(传递给)别人的程序,别人的程序再通过我传入的示例来调用方法。 举例说明: //

/** ICallBack接口和Print类就好比是别人提供的程序 **/ //接口类 public interface ICallBack { public void execute(); } public class Printer { ICallBack icb; public Printer(ICallBack icb){ this.icb = icb; } //提供给外部去调用的。即提供一个接口,我不去具体实现它,由外部程序来实现。再在想要的时候去回调外部的方法。我没有实现接口,但我拥有一个是实现了接口的外部对象,我通过外部对象去调用外部的方法。 public void print(){ System.out.println("print start ========");//固定部分 icb.execute(); //抽象部分,由外界去实现 System.out.println("print end ========");//固定部分 } } /PrintHandle相当于外部类 public class PrintHandle { public void printf(){ System.out.println("printHandle print"); } public void go(){//创建了一个匿名类对象并传递给Printer,通过内部类Printer也可以调用我的printf()方法了,这叫回调 Printer print = new Printer(new ICallBack() { @Override public void execute() { printf(); } }); print.print(); } public static void main(String[] args) { PrintHandle printHandle = new PrintHandle(); printHandle.go(); } }

另外再写一个测试方法运行时间的类

public class TestTime { public Long getTime(ICallBack icb){ long start = System.currentTimeMillis(); icb.execute(); long end = System.currentTimeMillis(); return end-start; } } //现在我要测试运行一遍test()方法需要多长时间 public class TestTimeMain { public void test() {//需要测试的方法 List<Map<String,String>> list = new ArrayList<>(); for (int i = 0; i < 10000; i++){ HashMap map = new HashMap<String,String>(); list.add(map); } } public static void main(String[] args) { TestTimeMain main = new TestTimeMain(); TestTime testTime = new TestTime(); //测试test()方法运行的时间,只要将这个方法放到匿名内部类的execute()里面就可以了 long time = testTime.getTime(new ICallBack() { @Override public void execute() { main.test(); } }); System.out.println(time); } }

回调的另一种方式:我称之为自身回调。 有两个接口worker(工人)和programmer(程序员),这两个接口都有一个work()方法,现有一个员工,要求它以某种方式实现这两个接口。这只能通过内部类来实现

Interface Worker{ void work(); } Interface Programmer{ void work(); } class Employee implements Worker{ public void work(){//作为工人在工作 System.out.println("worker is working"); } public void code(){//作为程序员在编程 System.out.println("programmer is programming"); } class Clousure implements Programmer{ public void work(){ code(); } } private class Clousure implements Programmer{ public void work(){ code(); } } public Programmer getCallBack(){ return new Clousure(); } public static void main(String[] args) { Employee employee = new Employee(); employee.work(); employee.getCallBack().work(); } }
转载请注明原文地址: https://www.6miu.com/read-25739.html

最新回复(0)