RxJava出現在我們的視線已經很久了,我自己也有閱讀過非常多的文章,談不上精通,但是勉強稱得上會一些簡單的使用,近日總是對這種響應式的編程,對RxJava魂牽夢繞,深刻的感覺到自己對泛型的認識,理解不到位,對RxJava的核心,觀察者模式有很多的不理解,導致在編碼或者說思想上達不到自己想要的效果
So,想著既然要學RxJava,自己對泛型的認識又不夠,就決定深入研究一下RxJava的源碼對泛型的使用,在探究源碼的過程中去理解泛型,去使用泛型,在泛型的學習中理解掌握RxJava,算是一種互補吧
再此默認大家已經會簡單使用RxJava,并且對RxJava的操作符(Operation)有一些了解
什么叫做響應式編程,什么叫做觀察者模式,什么又叫做事件,什么叫做消費,我談一下我自己的理解,如有不恰當之處,請大家指正(輕噴),這篇文章我也會長期更新下去,每次都會涉及到RxJava的操作符和自己去編寫這些操作符的實現
響應式編程:
與我們傳統(tǒng)編碼(函數式編程)不一樣,傳統(tǒng)編碼是做完這件事之后做另外一件事,給人的感覺都是單線程的,可能會開新線程去
處理耗時操作,在處理完成之后通過回調去處理之后的事情
而響應式編程提供給我們的是一種不一樣的思想,在響應式編程的世界中一切執(zhí)行流程都是基于事件的,已事件為驅動
觀察者模式:
觀察者模式是這樣子的,我先舉個例子看大家能不能理解
老師在講臺上講課,而所有的學生都會觀察著老師的一舉一動,而老師每產生一個事件(比如說在黑板上寫下一串公式),則對應著所有的學生都觀察到了老師的這一舉動,自己則在自己的筆記本中記錄,大腦中進行思考.而老師卻不關心自己的學生對這一舉動做什么事.
好了,例子就是這樣的,我們來分析一下這個例子跟觀察者模式有個什么關系?
這個例子中,老師可以產生事件,學生觀察著老師,而老師在產生事件之后咳嗽一下,通知所有的學生,我剛才做了什么事,你們應該也需要做點自己的事情
而這就產生了幾個概念,觀察者,被觀察者,事件,事件的處理與消費
被觀察者中存在觀察者的引用,即教師知道自己要通知的學生都有誰
被觀察者在產生事件之后通知觀察者,即教師產生事件之后通知每一位觀察著自己的學生
RxJava是對觀察者模式的一種高級運用,或者說是一種升級,他把觀察者模式具體化,更加明確了各個對象之間的關系
四個基本概念:
Observable (可觀察者柳琢,即被觀察者)累贤、
Observer (觀察者)秩冈、
subscribe (訂閱)弄屡、事件享完。
Observable 和 Observer 通過 subscribe() 方法實現訂閱關系匣掸,從而 Observable 可以在需要的時候發(fā)出事件來通知 Observer。
談完了響應式的一些東西,我覺得既然要討論學習泛型的使用,我們就把泛型的一些概念也揪出來瞅一下
泛型分為:
1 : 自定義泛型接口 interface Observer<T>
2 : 泛型類 class ImplObserver<T> implements Observer<T>
3 : 泛型方法 <T> Observer<T> call(T t)
說一下泛型的作用域
如果將泛型聲明放在泛型接口,泛型類上,則該泛型在該類中就是確定的了,如果將泛型聲明放在了泛型方法上,則該泛型只在該方法中有效,如果泛型方法上聲明的泛型類型和類或接口中聲明的泛型一致,則會在該方法中隱藏類或接口上的泛型
貼個代碼看一下
將泛型聲明放在接口
public interface Observable<T> {
public T call();
}
將泛型聲明放在方法
public interface Observable2 {
<T> T call(T t);
}
泛型聲明在接口或類上,則類或接口中的方法均可使用T類型
public class ImplObservable<T> implements Observable<T>{
@Override
public T call() {
// TODO Auto-generated method stub
return null;
}
}
泛型聲明在方法上,則除去該聲明有T泛型的方法之外,其他方法不識別T類型
public class ImplObservable2 implements Observable2{
@Override
public <T> T call(T t) {
// TODO Auto-generated method stub
return null;
}
}
public static void main(String[] args) {
//將泛型聲明在接口上或聲明在類上
Observable<Student> observer = new ImplObservable<Student>();
Student student = observer.call();
//將泛型聲明在方法上
ImplObserver2 Observable2 = new ImplObservable2();
Student student2 = observer2.call(new Student());
}
大概了解一下泛型的作用域和泛型的類型之后,我們現在有這么一個需求
我給你一個對象,你能夠觀察著該對象,即一個觀察者中存在著該對象的引用,并且將該觀察者返回給我
我剛開始是這么想的,我們看一下有沒有什么問題
public class ImplObservable<T> implements Observable<T>{
T t;
public ImplObservable(T t){
this.t = t;
}
}
看代碼的話好像確實也沒什么問題,我把泛型的聲明放在了類上,那我這個類中都是可以識別T類型的,那我在創(chuàng)建對象的時候傳入T好像也沒什么不對,一樣完成了需求,我們回到創(chuàng)建該對象的main方法中去看一看,創(chuàng)建方法變成了這樣
ImplObservable<Student> observer = new ImplObservable<>(new Student());
如果我把<>刪除掉,則編譯器會給我們這樣一個警告
Type safety: The expression of type ImplObservable needs unchecked conversion to conform to ImplObservable<Student>
類型不安全?怎么會不安全?并沒有報錯啊..
事情是這樣的,在ImplObserver中,我們將泛型聲明放在了類上,在該類中都可以識別T類型了,但是,構造方法接受一個T類型,如果你在創(chuàng)建該對象的時候,沒有向該類聲明T類型究竟屬于哪種類型,就直接傳遞了一個實際類型過去,問題就像這樣,教室接受所有類型過來,可能是教師,也可能是學生,但是,你在創(chuàng)建該教室的時候,你對教室接受的類型進行了限制,但是你又沒有通知教室說教室準確的要接受哪種類型的對象,這就會造成泛型不安全
我去翻了翻Rxjava的源碼,他將Observable這個對象的構造函數的訪問權限降低了,不在他包下都不可以創(chuàng)建這個對象,但是他提供了一個create方法去創(chuàng)建,我們也來模仿一下
public class ImplObservable<T> implements Observable<T>{
T t;
private ImplObservable(T t){
this.t = t;
}
public static <T> Observable<T> create(T t) {
return new ImplObservable<T>(t);
}
}
創(chuàng)建方法變成了這樣
Observable<Student> create = ImplObservable.create(new Student());
這樣我們在使用ImplObserver的時候就沒有對這個類的泛型進行明確說明,而是在create方法中進行了聲明,怎么聲明的? 這里面還有點門道,我們將create方法定義成了靜態(tài)方法,并且在該方法上聲明了T類型,這樣該方法的T類型就會隱藏掉類上的T類型,但是,我們的create方法做了這么一件事,將靜態(tài)方法的泛型,傳遞給了ImplObservable類上的泛型,并且返回創(chuàng)建好的ImplObservable泛型對象,此處的泛型類型為create方法聲明的泛型類型
是不是有點暈了?我當時也是暈的不行,迷糊過來之后也就那樣吧..如果有迷糊的朋友在下方評論吧,指出你的問題,我們一起討論
現在來考慮Rxjava寫代碼舒服的原因,全鏈式,全鏈式啊有木有,一條道走到黑,就是在不停的調用調用調用,不需要我們去考慮返回的對象是什么對象,只需要進行一系列操作就可以了,因為泛型已經幫助我們做了太多太多.
鏈式?哇,鏈式調用好像是很牛逼的,我們也來實現一下.
先說一下需求:
現在我給你一個student對象,你把這個對象給我通過某種規(guī)則給轉換成teacher對象,并且!
你要給我返回的觀察者不在是觀察學生了,而是,你剛才轉換成的teacher對象,并且!
我要求這些都是鏈式操作,起碼我看起來比較舒服,寫起來也比較開心!
說實話我是在學習泛型,研究Rxjava,我為啥非得給自己找不自在,提出的需求比較惡心就算了,還并且,倆并且,完成功能不就行了嗎?追求那么點的鏈式可能會給我的工作,我的業(yè)余時間帶來什么呢?
好了,我們來分析一下需求:
現在給一個student對象,要返回一個觀察著student的觀察者,我們通過上面的代碼可以這樣創(chuàng)建
ImplObservable.create(new Student());
現在要把這個學生通過某種規(guī)則轉換成teacher
做一個接口回調,傳遞學生類型進去,返回老師類型,但是這倆類型不明確,應該用泛型
我們模仿Rxjava的命名,也叫作Func1,
public interface Func1<T,R> {
R call(T t);
}
接口做好了,我們現在要在Observer中去定義一個方法,將T類型轉換成R類型,為了保持和Rxjava的一致,我們也叫作map
并且該方法要接受一種規(guī)則,一種能夠將T轉成R的規(guī)則
方法聲明也有了
<R> Observer<R> map(Func1<T,R> fun1);
我們要在ImplObserver中去實現該方法了
@Override
public <R> Observer<R> map(Func1<T, R> fun1) {
// TODO Auto-generated method stub
Observer<R> ob = ImplObservable.create(fun1.call(t));
return ob;
}
實現完了是這樣子的...
可能你看這點代碼會比較惡心,甚至會吐..
先喝杯水,起來晃悠一下,放松一會,希望你待會能打起十二分精神來讀接下來的一丁點篇幅
我會認真將自己的理解全部寫出來.
1:
創(chuàng)建被觀察者即ImplObservable.create(new Student());這時候我們要把Student這個對象存儲起來方便之后使用,但是create是靜態(tài)方法,
有聲明泛型T,但是ImplObservable又是被泛型聲明的泛型類,在create的時候去創(chuàng)建真正的被觀察者,并且將create方法攜帶的泛型類型帶過去,即被觀察者中的泛型來自于create方法的泛型.
而ImplObservable的構造方法要求傳入一個T類型,并且該類中存在一個T t的引用,即保存create方法傳遞過來的實際對象的引用
現在我們搞清楚了一個被觀察者中的實際對象(T對象)究竟存儲在了哪,一個成員變量T t中
2:
現在我們要想辦法把一個存儲有t對象的被觀察者轉換成一個存儲有另外一個t對象的被觀察者,我們提供一個map操作,代表類型的轉換操作
map要怎么實現是我們現在重點思考的問題
既然ImplObservable中可以存儲t對象,一個ImplObservable對應一個T類型,也就意味著一個ImplObservable存儲的這個t對象的類型已經確定,
那么我們要怎么把一個T對象轉換成R對象,轉換規(guī)則是怎么樣的
public interface Func1<T,R> {
R call(T t);
}
定義這么一個接口,接受一個T類型,返回一個R類型,在call方法中編寫轉換規(guī)則.
那么map方法就必然要接受一個接口了,即轉換規(guī)則
我們暫且這樣定義map方法
<R> Observable<R> map(Func1<T,R> fun1);
既然map方法也有了轉換的規(guī)則
map的實現就這樣了
@Override
public <R> Observable<R> map(Func1<T, R> fun1) {
Observable<R> ob = ImplObservable.create(fun1.call(t));
return ob;
}
至于為什么這么做?
現在我們知道ImplObservable.create方法接受一個T類型,并且把T類型存儲到當前對象中去,叫做t,這里是沒毛病的
我們來回想一下Func1這個接口的泛型聲明,接受T,返回R.
call方法接受T,返回R
這就意味著我們的ImplObservable.create方法接受的就是一個R類型!!!
并且ob對象中存儲的那個T t類型,實際上就應該是R r對象,即Teacher對象
這時候我們返回了ob,一個存儲有R(teacher)對象的被觀察者
至此,student轉換為teacher才真正結束.
好像是有點暈,好吧,回頭我畫個圖在說一下....
好了放松一下吧...確實比較惡心,也有點繞口,燒腦,但是想通了也就是那么一回事...
現在再來定義一個操作符,我們就結束今天這篇文章了
需求是這樣的
我需要在被觀察者的執(zhí)行過程中改一下被觀察者中存在的對象的屬性
并且不能破壞鏈式
我只是修改屬性,我要的還是該被觀察者
分析一下:
一個接口回調,需要把被觀察者保存的對象給傳遞回來,返回的結果不關心,即(void)
代碼實現:
//聲明下一步做的事
Observable<T> doOnNext(Action<T> action);
//定義泛型接口
public interface Action<T> {
void callAction(T t);
}
實現doOnNext方法
@Override
public Observable<T> next(Action<T> action) {
action.callAction(t);
return this;
}
解釋一下
當前被觀察者中已經存在T對象的引用即t,只需要將t回調過去,在外部類中進行修改,
但是被觀察者是不改變的,直接返回this就可以了.
最后上一下測試代碼
public static void main(String[] args) {
Student student = new Student();
System.out.println("創(chuàng)建好student : " + student);
final Teacher teacher = new Teacher();
System.out.println("創(chuàng)建好teacher : " + teacher);
ImplObservable.create(student)
.map(new Func1<Student, Teacher>() {
@Override
public Teacher call(Student t) {
// TODO Auto-generated method stub
System.out.println("student hashcode : " + t);
System.out.println("teacher hashcode : " + teacher);
return teacher;
}
})
.doOnNext(new Action<Teacher>() {
@Override
public void callAction(Teacher t) {
// TODO Auto-generated method stub
System.out.println("teacher hashcode2 : " + t);
}
});
}
輸出結果
創(chuàng)建好student : com.lxkj.learn.Student@95cfbe
創(chuàng)建好teacher : com.lxkj.learn.Teacher@1950198
student hashcode : com.lxkj.learn.Student@95cfbe
teacher hashcode : com.lxkj.learn.Teacher@1950198
teacher hashcode2 : com.lxkj.learn.Teacher@1950198
在RxJava的世界里,你可以把所有的被觀察者想成一條河流,既然是河流,他就可以被過濾,攔截,轉換,變換,修飾,處理,甚至于合并等一系列操作,河流在流動的過程中觀察者是不會處理的,只有在河流抵達了終點,即被觀察者訂閱了觀察者之后,觀察者才會對最終的這股河流進行處理.
RxJava之所以強大,是因為他對河流在流動過程中提供了太多太多的操作符,我們能想到的操作,在RxJava中基本都有某種操作符來處理,而RxJava不太贊成我們自己去定義操作符,因為定義這些操作符的邏輯確實太繞了,就像上面我們自己定義map操作符一樣,真的是非常難受,一不小心可能就會造成一連串的錯誤
這篇文章的重點從RxJava的四個概念開始,到結束,全都是我和同學一點點分析總結出來的,跟源碼比起來搓一萬倍,
只能說跟大家一塊兒感悟一下Rxjava的魅力和他的執(zhí)行流程,對自己理解Rxjava也算是一點幫助吧,
最后想說的是,泛型這玩意,是真厲害...
以后沒事的話就去研究幾個操作符,對自己理解泛型理解Rx都是有很大幫助的.我也非常樂意分享這些心得,也希望大家能批評文章中的錯誤,我會認真吸取經驗反哺大家的..