java閉包和回撥淺析
按照概念,閉包(Closure)是一種能被呼叫的物件,它儲存了建立它的作用域的資訊。
我們先來看下面的例子:
//Programmer.java
interface inter{
void work();
}
public class Programmer{ //並沒有實現介面inter
private String name;
public Programmer(){}
public Programmer(String name){
this.name = name;
}
...//此處省略了name屬性的setter和getter方法
public void work(){
System.out.println(name+"在敲程式碼");
}
}
//Teacher.java
public class Teacher extends Programmer implements inter{
public void work(){
System.out.println(super.name+"在講課");
}
}
//Test.java
public class Test{
public static void main(String[] args){
Programmer p = new Programmer("老朱");
p = new Teacher();
p.work();
}
}
此時輸出的只能是:老朱在講課。
但是實際上我們需要通過 t 來呼叫相關函式來輸出:老朱在敲程式碼。
現在,我們考慮一種情況:
我們新建一個 TestOther.java檔案,類TestOther繼承類Programmer:
在類TestOther內部定義一個內部類(我們規定,該內部類只由該外部類所屬,用private修飾),
現在介面該由誰來實現呢?
我們最後都是要到main函式中 new一個TestOther物件,然後利用物件來呼叫Programmer中的work()函式以及TestOther中的work()函式的。現在TestOther已經繼承了Programmer類,就不能在TestOther中直接定義一個work()函式了,所以介面不能由該類來實現。那我們就直接定義一個teach()函式,功能與TestOther中的work()函式相同。
我們知道,非靜態內部類的方法可以訪問外部類的成員。
所以,如果我們定義一個Closure內部類,然後在該類的內部定義一個work()函式(介面由內部類來實現),函式體用來回撥外部類的成員teach();
這樣 ,在另一個.java檔案TestAll.java的main函式之中,我們通過TestOther的物件,可以直接呼叫work()函式(繼承過來的,也就是父類Programmer的work()函式),就會輸出:**在敲程式碼;
現在,如果讓TestOther中的teach()函式變為public函式,事情就簡單多了,直接利用上面的物件來呼叫即可輸出:**在講課。
但是為了體現java程式設計的靈活性,(在不改變teach()函式的private屬性的情況下也不得不這麼做):
現在,非靜態內部類Closure內部的work()函式也回撥了teach()函式,而且,考慮到內部類是private修飾的,所以不能在外部類外部進行例項化內部類物件來呼叫它定義的介面inter的work()函式。
所以我們在外部類中再定義一個public函式getCallbackReference(),來返回new Closure();即可,那麼根據向上轉型的引數統一化,我們利用介面inter作為函式的返回值型別。
這樣,我們就可以在TestAll.java檔案的main函式中利用TestOther的物件來呼叫public函式getCallbackReference(),利用其返回值(例項化內部類物件)來呼叫非靜態內部類的work()函式,這樣通過回撥teach()函式,就可以輸出:**在講課。
下面給出相關程式碼:
//TestOther.java
public class TestOther extends Programmer{
public TestOther(){}
public TestOther(String name){
super(name);
}
/*
定義一個private函式,功能也是輸出:**在講課
*/
private void teach(){
System.out.println(getName()+"在講課");
}
/*
非靜態內部類回撥外部類成員實現work()方法,非靜態內部類的作用僅僅是向客戶類
提供過一個回撥外部類的途徑,但該類為private修飾,不能在外部直接例項化物件,
所以需要再定義下面的getCallbackReference()函式
*/
private class Closure implements inter{
public void work(){
teach();
}
}
/*
定義一個可以被外部呼叫的public函式,用向上轉型的規範返回非靜態內部類的例項化物件
*/
public inter getCallbackReference(){
return new Closure();
}
}
//TestAll.java
public class TestAll{
public static void main(String[] args){
TestOther tp = new TestOther(""老朱);
tp.work(); //直接呼叫TestOther類從Programmer類繼承到的work()方法
//表面上呼叫的是Closure的work()方法,實際上是回撥TestOther的teach()方法
tp.getCallbackReference().work();
}
}
實際上,java並不能顯示地支援閉包,但是對於非靜態內部類而言,它不僅記錄了其外部類地詳細資訊,還保留了一個建立非靜態內部類物件的引用,並且可以直接呼叫外部類的private成員,因此,可以把非靜態內部類當成物件導向領域的閉包。
通過這種仿閉包的非靜態內部類,可以很方便地實現回撥功能,回撥就是某個方法一旦獲得了內部類物件的引用後,就可以在合適的時候反過來呼叫外部類例項的方法。所謂回撥,就是允許客戶類(main函式所在的類)通過內部類引用來呼叫其外部類的方法。這是一種非常靈活的功能。
下面給出另一個相似的例子:
interface inter{
void fun();
}
class A implements inter
{
private int i = 0;
public void fun()
{
i++;
System.out.println(i);
}
}
class B
{
public void fun()
{
System.out.println("B、fun()");
}
static void fB(B b)
{
b.fun();
}
}
class C extends B{
private int i = 0;
public void fun()
{
super.fun();
i++;
System.out.println(i);
}
private class Closure implements inter
{
public void fun()
{
C.this.fun();
}
}
public inter getCallbackReference()
{
return new Closure();
}
}
class M
{
private inter in;
M(inter i){
in = i;
}
void funM(){
in.fun();
}
}
public class TestDemo {
public static void main(String[] args){
A a = new A();
C c = new C();
B.fB(c);
M m1 = new M(a);
M m2 = new M(c.getCallbackReference());
m1.funM();
m1.funM();
m2.funM();
m2.funM();
}
}
輸出結果是:
B、fun()
1
1
2
B、fun()
2
B、fun()
3
首先,A簡單的實現了介面inter與相關方法,在這裡起到一個對比的作用而已。
然後定義一個B類同樣實現了一個fun()方法但是這個與介面中的increment()沒有任何關係,因為這個類自己實現的,並沒有實現這個介面,而靜態方法fB()也只是為了測試一下B類自己的fun()方法。
而C類繼承自B類。同樣寫了一個fun()方法,覆蓋了父類的fun()方法,但是函式內部還是呼叫了父類的fun()方法。
接著,在C類中定義了一個private修飾的非靜態內部類Closure(也就是閉包的具體實現了)。
內部類實現了介面inter並且直接呼叫外部類的方法作為具體的實現。
內部類實現inter介面很關鍵,這樣就給外部留下了一個通道,能夠接受這個內部類。
最後類C的後面留下了一個鉤子,即getCallbackReference()方法,它返回一個內部類的物件,實現了內部與外部的連結,同時有保證了內部類的安全,因為只有Callee2的物件可以訪問與呼叫這個內部類的方法,而其他的類都無權訪問,即使是基類介面物件。
而後面的定義的M類,起到的是喚醒作用,利用有參建構函式,通過接受不同的介面物件傳入形參,實現不同的操作(向上轉型規範)。
但還有一個作用是等待接受一個內部類物件,來產生回撥,因為一旦傳入的是具有內部類的類C例項化物件呼叫的getCallbackReference()函式,就可以實現回撥作用。現在大家再回頭看一下輸出就能夠明白了。
假裝你回頭看了,在main()方法中,首先是建立物件與宣告,然後是呼叫了B類的靜態方法fB(),(B是C的父類)傳入的是一個C的例項化物件,此時無法觸發回撥,只是正常的輸出(看起來很像回撥,實際上只是執行了類B的靜態成員fB()而已)
然後,在對C的初始化時傳入的是一個Closure物件從而產生了回撥。
以上就是java的閉包與回撥機制,結合後面的內容會有更多意想不到的作用。
相關文章
- 【JavaSE】java實現閉包與回撥Java
- 淺析Block閉包BloC
- 回撥函式 與 函式閉包函式
- PHP Clourse(閉包類) 淺析PHP
- Swift 中如何利用閉包實現非同步回撥?Swift非同步
- 淺析微信支付:申請退款、退款回撥介面、查詢退款
- 【Javascript】淺析JS中閉包的來龍去脈JavaScriptJS
- [JS]回撥函式和回撥地獄JS函式
- 淺談匿名函式和閉包函式
- 閉包 | 淺談JavaScript閉包問題JavaScript
- java回撥函式-非同步回撥-簡明講解Java函式非同步
- java 回撥函式示例Java函式
- 淺談js閉包JS
- 淺析Java反射--JavaJava反射
- 淺析Java NIOJava
- 淺析JAVA反射Java反射
- java回撥函式機制Java函式
- 深入淺出Javascript閉包JavaScript
- Java快取淺析Java快取
- Java偏向鎖淺析Java
- 淺析Java斷言Java
- 淺析Java常量池Java
- 回撥和spring的LambdaSafe類Spring
- 【詳細、開箱即用】.NET企業微信回撥配置(資料回撥URL和指令回撥URL驗證)
- 淺淺的談一下回撥地獄的問題
- C++屌屌的觀察者模式-同步回撥和非同步回撥C++模式非同步
- java多型性淺析Java多型
- 淺析單例模式--Java單例模式Java
- 淺析IOC 和 DI
- 回撥方法
- 淺談JS作用域、this及閉包JS
- 淺析手機抓包方法實踐
- Java網路程式設計與NIO詳解4:淺析NIO包中的Buffer、Channel 和 SelectorJava程式設計
- JAVA動態繫結淺析Java
- 淺析Java8 Stream原理Java
- 淺析Java Web框架技術JavaWeb框架
- 淺析java中的IO流Java
- java高階用法之:JNA中的回撥Java