Android Model正確使用姿勢(shì)AutoValue
最近看到幾篇博客是關(guān)于AutoValue的,然后自己十分喜歡,一下子覺的這樣寫代碼很優(yōu)雅,所以決定自己也寫一篇文章介紹下AutoValue。
本文最先發(fā)表于Github,如有轉(zhuǎn)載,請(qǐng)注明轉(zhuǎn)載出處。
前言
首先說Android Model,在開發(fā)中網(wǎng)絡(luò)請(qǐng)求,以及數(shù)據(jù)庫操作等,我們都會(huì)定義一個(gè)Model,不同人對(duì)這個(gè)的說法不一樣,比如有Entry,Bean,Pojo。
然后開發(fā)的過程中會(huì)遇到下面問題:
構(gòu)成方法:自定義構(gòu)造方法,如果實(shí)體比較復(fù)雜,可能會(huì)用到工廠模式或者是建造者模式
序列化:比如實(shí)現(xiàn)Serializable接口,Parcelable接口。
Json解析:有時(shí)候直接使用的是json數(shù)據(jù),比如@SerializedName注解。
自定義方法:對(duì)Model的字段有setter,getter方法,toString的實(shí)現(xiàn),在處理hash的時(shí)候,需要實(shí)現(xiàn)equals和hashcode方法。
以上這么問題,其實(shí)在Eclipse和Android Studio中都是有快捷功能幫我們自動(dòng)生成,后面的代碼示例,就是我用Android Studio自動(dòng)生成的。
比如下面一個(gè)User類是我們的本次示例的一個(gè)Model,如果按照正常的寫法,是這樣的。
publicabstractclassUserimplementsSerializable{@SerializedName("id")privateintid;@SerializedName("name")privateString name;publicintgetId() {returnid;? ? }publicvoidsetId(intid) {this.id = id;? ? }publicStringgetName() {returnname;? ? }publicvoidsetName(String name) {this.name = name;? ? }@Overridepublicbooleanequals(Object o) {if(this== o)returntrue;if(o ==null|| getClass() != o.getClass())returnfalse;? ? ? ? User user = (User) o;if(id != user.id)returnfalse;returnname !=null? name.equals(user.name) : user.name ==null;? ? }@OverridepublicinthashCode() {intresult = id;? ? ? ? result =31* result + (name !=null? name.hashCode() :0);returnresult;? ? }@OverridepublicStringtoString() {return"User{"+"id="+ id +", name='"+ name +'\''+'}';? ? }}
簡介
官方文檔給出的解釋是這樣的,大致意思是說是一個(gè)生成Java不可變的值類型工具,仔細(xì)研讀源代碼后,使用的技術(shù)是Java Apt,這個(gè)后面再做詳細(xì)解釋。
AutoValue - Immutable value-type code generation for Java 1.6+.
簡單使用
按照上面的例子,如果是AutoValue,代碼是這樣的。
首先需要在Android項(xiàng)目里面引入apt功能,在項(xiàng)目根目錄的gradle中添加,
buildscript{
repositories {
jcenter()
}dependencies{? ? ? ? classpath'com.android.tools.build:gradle:2.2.2'// 引入apt插件? ? ? ? classpath'com.neenbedankt.gradle.plugins:android-apt:1.8'}}
其次在module(一般是app目錄)中g(shù)radle使用apt插件。
apply plugin:'com.android.application'apply plugin:'com.neenbedankt.android-apt'
最后加入AutoValue依賴。
dependencies{? ? provided'com.google.auto.value:auto-value:1.3'apt'com.google.auto.value:auto-value:1.3'}
修改User類,如下所示,User已經(jīng)變成了一個(gè)抽象類,類似于使用Retrofit一樣,申明已經(jīng)變成了一個(gè)接口,然后實(shí)現(xiàn)類是由AutoValue生成的代碼。
importcom.google.auto.value.AutoValue;@AutoValuepublicabstractclassUser{publicabstractintid();publicabstractStringname();publicstaticUsernewInstance(intid, String name) {returnnewAutoValue_User(id, name);? ? }}
我們可以看看AutoValue到底干了什么?
AutoValue會(huì)自動(dòng)生成一個(gè)AutoValue_User,這個(gè)類是繼承了上面申明的User類,這個(gè)是默認(rèn)default的訪問權(quán)限,那么在其他package中是無法訪問的,這樣在其他代碼里面也不會(huì)看到這么奇怪的名字。
同時(shí)所有的字段都是final類型,如果字段是對(duì)象類型的,那么還不能為空,這個(gè)問題先保留,后面再做詳解。因?yàn)樯昝鞯氖莊inal類型,那么所有的字段都是沒有setter方法的。
代碼里同時(shí)也實(shí)現(xiàn)了equals、hashcode、toString方法。
finalclass AutoValue_User extends User {privatefinalintid;privatefinalString name;? AutoValue_User(intid,? ? ? String name) {this.id = id;if(name ==null) {thrownewNullPointerException("Null name");? ? }this.name = name;? }@Overridepublicintid() {returnid;? }@OverridepublicStringname() {returnname;? }@OverridepublicStringtoString() {return"User{"+"id="+ id +", "+"name="+ name? ? ? ? +"}";? }@Overridepublicbooleanequals(Object o) {if(o ==this) {returntrue;? ? }if(oinstanceofUser) {? ? ? User that = (User) o;return(this.id == that.id())? ? ? ? ? && (this.name.equals(that.name()));? ? }returnfalse;? }@OverridepublicinthashCode() {inth =1;? ? h *=1000003;? ? h ^=this.id;? ? h *=1000003;? ? h ^=this.name.hashCode();returnh;? }}
Immutable/Value types
剛剛上面說到,所有的字段都是final類型,那么而且實(shí)現(xiàn)類也是final的,有個(gè)專業(yè)術(shù)語叫Immutable。
Immutable/Value types 這個(gè)概念對(duì)有些朋友來說可能還比較陌生,簡單來說就是一個(gè)數(shù)據(jù)對(duì)象一旦構(gòu)造完成,就再也無法修改了。
這樣有什么好處呢?最大的好處就是多線程訪問可以省去很多同步控制,因?yàn)樗鼈兪遣豢勺兊模坏?gòu)造完成,就不會(huì)存在多線程競(jìng)爭訪問問題了。多線程最麻煩的處理就是控制好讀寫問題,如果大家都是讀,那么就不存控制了,所以省去了很多同步操作。
更多關(guān)于Immutable 的介紹,可以參閱wiki。
舉個(gè)Java中的例子:String和StringBuilder,String是immutable的,每次對(duì)于String對(duì)象的修改都將產(chǎn)生一個(gè)新的String對(duì)象,而原來的對(duì)象保持不變,而StringBuilder是mutable,因?yàn)槊看螌?duì)于它的對(duì)象的修改都作用于該對(duì)象本身,并沒有產(chǎn)生新的對(duì)象。
Immutable objects 比傳統(tǒng)的mutable對(duì)象在多線程應(yīng)用中更具有優(yōu)勢(shì),它不僅能夠保證對(duì)象的狀態(tài)不被改變,而且還可以不使用鎖機(jī)制就能被其他線程共享。
總結(jié)下Immutable對(duì)象的優(yōu)缺點(diǎn):
優(yōu)點(diǎn)
Immutable對(duì)象是線程安全的,可以不用被synchronize就在并發(fā)環(huán)境中共享
Immutable對(duì)象簡化了程序開發(fā),因?yàn)樗鼰o需使用額外的鎖機(jī)制就可以在線程間共享
Immutable對(duì)象提高了程序的性能,因?yàn)樗鼫p少了synchroinzed的使用
Immutable對(duì)象是可以被重復(fù)使用的,你可以將它們緩存起來重復(fù)使用,就像字符串字面量和整型數(shù)字一樣。你可以使用靜態(tài)工廠方法來提供類似于valueOf()這樣的方法,它可以從緩存中返回一個(gè)已經(jīng)存在的Immutable對(duì)象,而不是重新創(chuàng)建一個(gè)。
缺點(diǎn)
Immutable也有一個(gè)缺點(diǎn)就是會(huì)制造大量垃圾,由于他們不能被重用而且對(duì)于它們的使用就是”用“然后”扔“,字符串就是一個(gè)典型的例子,它會(huì)創(chuàng)造很多的垃圾,給垃圾收集帶來很大的麻煩。當(dāng)然這只是個(gè)極端的例子,合理的使用immutable對(duì)象會(huì)創(chuàng)造很大的價(jià)值。
高級(jí)使用
Nullable
上面說過如果類中有對(duì)象類型的成員變量,那么是為非空的,但是在實(shí)際情況下,有的字段的是值就是為null,所以在申明時(shí)候可申明為Nullable就可以了。
importandroid.support.annotation.Nullable;importcom.google.auto.value.AutoValue;importcom.google.gson.annotations.SerializedName;@AutoValuepublicabstractclassNullableUser{@SerializedName("id")publicabstractintid();@Nullable@SerializedName("name")publicabstractStringname();publicstaticNullableUsernewInstance(intid, String name) {returnnewAutoValue_NullableUser(id, name);? ? }}
生成代碼:
finalclass AutoValue_NullableUser extends NullableUser {privatefinalintid;privatefinalString name;? AutoValue_NullableUser(intid,@NullableString name) {this.id = id;this.name = name;? }}
測(cè)試用例
@Test(expected = NullPointerException.class)publicvoidtestUserNullPointException()throwsException {? ? ? ? User.newInstance(100,null);? ? }@TestpublicvoidtestUserNullable() {? ? ? ? NullableUser user = NullableUser.newInstance(100,"test");? ? ? ? System.out.println("user = "+ user);? ? ? ? Assert.assertEquals(user.id(),100);? ? ? ? Assert.assertEquals(user.name(),"test");? ? }
Gson序列化
Gson 使用比較麻煩,在普通的Model中,只需要在字段上面添加 @SerializedName注解即可。但是使用AutoValue,稍微有點(diǎn)繁瑣。
首先需要引入一個(gè)依賴包,這里是Auto value gson Github。
provided'com.ryanharter.auto.value:auto-value-gson:0.4.4'apt'com.ryanharter.auto.value:auto-value-gson:0.4.4'
其次申明的抽象類中,每個(gè)方法上面添加對(duì)應(yīng)的注解,然后再添加一個(gè)typeAdapter方法,申明這個(gè)方法,Gson就會(huì)根據(jù)這個(gè)找到對(duì)應(yīng)的adapter,如下所示。
@AutoValuepublicabstractclassUser{@SerializedName("id")publicabstractintid();@SerializedName("name")publicabstractStringname();publicstaticUsernewInstance(intid, String name) {returnnewAutoValue_User(id, name);? ? }publicstaticTypeAdaptertypeAdapter(Gson gson) {returnnewAutoValue_User.GsonTypeAdapter(gson);? ? }}
typeAdapter方法模板如下,T就是你當(dāng)前Model的名字,寫完以后會(huì)出現(xiàn)錯(cuò)誤,沒事重新編譯下就好了,這樣就會(huì)重新生成了代碼。
publicstaticTypeAdaptertypeAdapter(Gson gson) {returnnewAutoValue_T.GsonTypeAdapter(gson);}
第三申明一個(gè)TypeAdapterFactory的一個(gè)實(shí)現(xiàn)類,這個(gè)類是abstract的,AutoValue也會(huì)自動(dòng)生成其實(shí)現(xiàn)類。
@GsonTypeAdapterFactorypublicabstractclassMyAdapterFactoryimplementsTypeAdapterFactory{publicstaticTypeAdapterFactorycreate() {returnnewAutoValueGson_MyAdapterFactory();? ? }}
最后是單元測(cè)試,在json字符串轉(zhuǎn)Model的時(shí)候,會(huì)使用一個(gè)Gson對(duì)象,這個(gè)對(duì)象不是平常使用的對(duì)象,需要自定義配置一些東西,然后這里就用到了上面所申明的MyAdapterFactory。
@TestpublicvoidtestUserToJson() {? ? ? ? User user = User.newInstance(100,"test");? ? ? ? String json =newGson().toJson(user);? ? ? ? System.out.println(json);? ? ? ? Assert.assertEquals("{\"id\":100,\"name\":\"test\"}", json);? ? }@TestpublicvoidtestUserParseFromJson() {? ? ? ? String json ="{\"id\":100,\"name\":\"test\"}";// 自定義的Gson對(duì)象,需要配置 MyAdapterFactoryGson gson =newGsonBuilder().registerTypeAdapterFactory(MyAdapterFactory.create()).create();? ? ? ? User user = gson.fromJson(json, User.class);? ? ? ? System.out.println(user);? ? ? ? Assert.assertNotNull(user);? ? ? ? Assert.assertEquals(user.name(),"test");? ? ? ? Assert.assertEquals(user.id(),100);? ? ? ? NullableUser nullableUser = gson.fromJson(json, NullableUser.class);? ? ? ? System.out.println(nullableUser);? ? ? ? Assert.assertNotNull(nullableUser);? ? ? ? Assert.assertEquals(nullableUser.name(),"test");? ? ? ? Assert.assertEquals(nullableUser.id(),100);? ? }
Serializable & Parcelable
Serializable是Java自帶的序列化方式,和AutoValue結(jié)合不影響原先使用,只需要在申明的Model中實(shí)現(xiàn)Serializable接口即可。
Parcelable是Android提供的序列化方式,如果需要和AutoValue結(jié)合使用,和Serializable基本差不多,實(shí)現(xiàn)相關(guān)接口,然后在Gradle文件引入相關(guān)apt依賴即可。
apt'com.ryanharter.auto.value:auto-value-parcel:0.2.5'// OptionallyforTypeAdapter support? ? // compile'com.ryanharter.auto.value:auto-value-parcel-adapter:0.2.5'
上面的auto-value-parcel-adapter是可選項(xiàng),是auto-value-parcel提供自定義類型轉(zhuǎn)化,相關(guān)使用可以參見Github地址。
檢查下Autovalue自動(dòng)給我們實(shí)現(xiàn)的代碼,果然不出所料,全部自動(dòng)生成了。
finalclass AutoValue_User extends $AutoValue_User {publicstaticfinalParcelable.Creator CREATOR =newParcelable.Creator() {@OverridepublicAutoValue_UsercreateFromParcel(Parcel in) {returnnewAutoValue_User(? ? ? ? ? in.readInt(),? ? ? ? ? in.readString()? ? ? );? ? }@OverridepublicAutoValue_User[]newArray(intsize) {returnnewAutoValue_User[size];? ? }? };? AutoValue_User(intid, String name) {super(id, name);? }@OverridepublicvoidwriteToParcel(Parcel dest,intflags) {? ? dest.writeInt(id());? ? dest.writeString(name());? }@OverridepublicintdescribeContents() {return0;? }}
Retrofit和Rxjava結(jié)合使用
Android 開發(fā)的時(shí)候,很多開發(fā)者使用Retrofit這個(gè)網(wǎng)絡(luò)庫,以及RxJava異步工具。下面舉例如何結(jié)合使用AutoValue,Retrofit,Rxjava。
這里有個(gè)獲取天氣的接口,返回的結(jié)果是json,我們用這個(gè)來測(cè)試下Retrofit。
// https://api.thinkpage.cn/v3/weather/now.json?key=x4qjfuniyu97mt9y&location=beijing&language=zh-Hans&unit=c{"results": [? ? {"location": {"id":"WX4FBXXFKE4F","name":"北京","country":"CN","path":"北京,北京,中國","timezone":"Asia/Shanghai","timezone_offset":"+08:00"},"now": {"text":"霾","code":"31","temperature":"10"},"last_update":"2016-12-02T14:45:00+08:00"}? ]}
申明Retrofit Api接口,一個(gè)普通的調(diào)用,一個(gè)是RxJava的方式。
publicinterfaceIWeatherApi{@GET("/v3/weather/now.json?key=x4qjfuniyu97mt9y&location=beijing&language=zh-Hans&unit=c")? ? Call getWeather();@GET("/v3/weather/now.json?key=x4qjfuniyu97mt9y&location=beijing&language=zh-Hans&unit=c")? ? Observable getWeatherWithRx();}
Retrofit 接口創(chuàng)建
publicclassRetrofitUtil{publicstatic TcreateApi(@NonNull Class tClass, Gson gson) {returnnewRetrofit.Builder()? ? ? ? ? ? ? ? .baseUrl("https://api.thinkpage.cn")? ? ? ? ? ? ? ? .client(newOkHttpClient.Builder().build())? ? ? ? ? ? ? ? .addConverterFactory(GsonConverterFactory.create(gson))? ? ? ? ? ? ? ? .addCallAdapterFactory(RxJavaCallAdapterFactory.create())? ? ? ? ? ? ? ? .build()? ? ? ? ? ? ? ? .create(tClass);? ? }}
Weather Model申明
publicabstractclassWeather{@SerializedName("results")publicabstractListresults();publicstaticTypeAdaptertypeAdapter(Gson gson) {returnnewAutoValue_Weather.GsonTypeAdapter(gson);? ? }}
測(cè)試用例,注意:Retrofit使用Gson和前面使用Gson使用方式一樣,需要自己自定義,不然無法解決json解析問題。
@TestpublicvoidtestRetrofitWithAutoValue() {? ? ? ? Gson gson =newGsonBuilder().registerTypeAdapterFactory(MyAdapterFactory.create()).create();? ? ? ? IWeatherApi weatherApi = RetrofitUtil.createApi(IWeatherApi.class, gson);try{// 同步調(diào)用Weather weather = weatherApi.getWeather().execute().body();? ? ? ? ? ? Assert.assertNotNull(weather);? ? ? ? ? ? System.out.println(weather);// Rxjava 使用weatherApi.getWeatherWithRx().subscribe(newAction1() {@Overridepublicvoidcall(Weather weather) {? ? ? ? ? ? ? ? ? ? System.out.println(weather);? ? ? ? ? ? ? ? }? ? ? ? ? ? });? ? ? ? }catch(IOException e) {? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? }
運(yùn)行結(jié)果,正常的返回天氣信息。
Weather{results=[ResultsItem{now=Now{code=31, temperature=9,text=霾}, lastUpdate=2016-12-02T14:15:00+08:00, location=Location{country=CN, path=北京,北京,中國, timezone=Asia/Shanghai, timezoneOffset=+08:00,name=北京,id=WX4FBXXFKE4F}}]}Weather{results=[ResultsItem{now=Now{code=31, temperature=9,text=霾}, lastUpdate=2016-12-02T14:15:00+08:00, location=Location{country=CN, path=北京,北京,中國, timezone=Asia/Shanghai, timezoneOffset=+08:00,name=北京,id=WX4FBXXFKE4F}}]}
相關(guān)插件
RoboPOJOGenerator
GsonFormat是一款A(yù)ndroid Studio的插件,它可以把json字符串,轉(zhuǎn)變成Model對(duì)象,很多人都喜歡用它。
但是如果使用了AutoValue,那么原先的插件就不能使用了,沒有關(guān)系,本來打算自己高仿GsonFormat重新寫了一個(gè)插件,以實(shí)現(xiàn)我們的需求,后面又發(fā)現(xiàn)有一款插件可以實(shí)現(xiàn)——RoboPOJOGenerator。
RoboPOJOGenerator使用,RoboPOJOGenerator Github地址
AutoValue plugin
上面我們發(fā)現(xiàn)有了json字符串,有時(shí)候還要寫factory和buildder方法,那么問題來了,沒有插件能幫我們實(shí)現(xiàn)這個(gè)步驟,然代碼更加的優(yōu)雅,開發(fā)更加高效?
答案是肯定的,Autovalue plugin就是干這個(gè)事的。
我們用剛剛上面的Weather做演示,相關(guān)演示:
原理介紹
本文重點(diǎn)介紹的AutoValue只是Google Auto中的一小部分,Auto中還有其他好玩的。
AutoFactory
AutoFactory和AutoValue類似,可以自動(dòng)幫助代碼生成工廠類,兼容Java 依賴注入標(biāo)準(zhǔn)(JSR-330)。
代碼示例
@AutoFactorypublicclassFactoryUser{privatefinalintid;privatefinalString name;publicFactoryUser(intid, String name) {this.id = id;this.name = name;? ? }publicintgetId() {returnid;? ? }publicStringgetName() {returnname;? ? }@OverridepublicStringtoString() {return"FactoryUser{"+"id="+ id +", name='"+ name +'\''+'}';? ? }}
生成后的代碼
publicfinalclassFactoryUserFactory{@InjectpublicFactoryUserFactory() {? }publicFactoryUsercreate(intid, String name) {returnnewFactoryUser(id, name);? }}
測(cè)試代碼
@TestpublicvoidtestFactoryUser() {? ? ? ? FactoryUser user =newFactoryUserFactory().create(100,"test");? ? ? ? System.out.println(user);? ? ? ? Assert.assertNotNull(user);? ? ? ? Assert.assertEquals(100, user.getId());? ? ? ? Assert.assertEquals("test", user.getName());? ? }
AutoService
AutoService比較簡單,就是在使用Java APT的時(shí)候,使用AutoService注解,可以自動(dòng)生成meta信息。
AutoCommon
這個(gè)是Google對(duì)Java Apt的一個(gè)擴(kuò)展,一般的在自己寫Apt的時(shí)候,都需要繼承AbstractProcessor,但是google對(duì)它進(jìn)行了擴(kuò)展,BasicAnnotationProcessor,如果你想自己寫個(gè)工具,那么就可以使用這個(gè)了。
給大家舉個(gè)栗子,Dagger當(dāng)初是Square公司受到Guice的啟發(fā),然后自己開發(fā)出一套依賴注入框架,當(dāng)時(shí)Dagger使用的是Java反射,大家知道Java反射的效率其實(shí)并不高。
再后來都到了AutoValue的啟發(fā),在Dagger的分支上切個(gè)新分支,開發(fā)出Dagger2,然后這個(gè)Dagger2是由Google維護(hù)的,我們可以在Dagger2的Github上面找到證據(jù)。
Auto相關(guān)使用
IntentBuilder
有時(shí)候幾個(gè)Activity之間相互跳轉(zhuǎn)的時(shí)候需要傳遞一些參數(shù),這些參數(shù)可以是基本類型,也有可能是復(fù)雜的類型,如果是負(fù)責(zé)的類型,必須要實(shí)現(xiàn)Serializable 或 Parcelable接口,上面也有介紹。
下面推IntentBuilder,IntentBuilder也是利用代碼生成的方法實(shí)現(xiàn)的。
Activity傳參
@IntentBuilderclass DetailActivity extends Activity {@ExtraString id;@Extra@NullableString title;@OverridepublicvoidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);? ? ? ? DetailActivityIntentBuilder.inject(getIntent(),this);// TODO use id and title}}// 調(diào)用方式startActivity(newDetailActivityIntentBuilder("12345")? ? ? ? .title("MyTitle")? ? ? ? .build(context))
Service傳參
@IntentBuilderclass DownloadService extends IntentService {@ExtraString downloadUrl;@OverrideprotectedvoidonHandleIntent(Intent intent) {? ? ? ? MyServiceIntentBuilder.inject(intent,this);? ? }}startService(newDownloadServiceIntentBuilder("http://google.com").build(context))
FragmentArgs
上面介紹了Activity、Service的傳參,但Fragment的傳參方式是不一樣的,還有需要提醒一句一般通過setter方法給Fragment傳參是不是正確的方式,必須通過setArgs的方式。
相關(guān)代碼示例:
@FragmentWithArgspublicclassMyFragmentextendsFragment{@Argintid;@OverridepublicvoidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);? ? ? ? FragmentArgs.inject(this);// inject 之后,就可以使用 id 了}}MyFragment fragment = MyFragmentBuilder.newMyFragment(101);
其他相關(guān)
Kotlin Data Class
Kotlin 是一個(gè)基于 JVM 的新的編程語言,由 JetBrains 開發(fā)。有機(jī)會(huì)可以向大家介紹這種語言。
Kotlin 中提供一種類似于AutoValue中的功能,Data Class表示這個(gè)類似是一個(gè)數(shù)據(jù)類型。
比如下面是kotlin中對(duì)Model的寫法,就是這么的簡單、明了、優(yōu)雅。
dataclassKotlinUser(valid:Int,valname:String)
Kotlin與Java是可以相互調(diào)用的。下面是Java的測(cè)試用例。
publicclassUserTest{@TestpublicvoidtestUser() {? ? ? ? KotlinUser user =newKotlinUser(100,"test");? ? ? ? System.out.println(user);? ? ? ? Assert.assertEquals(100, user.getId());? ? ? ? Assert.assertEquals("test", user.getName());? ? }}
我們可以反編譯Kotlin生成的class字節(jié)碼,看看這個(gè)中間到底發(fā)生了什么,很明顯Kotlin做了很多的語法糖,這里編譯器生成的代碼和上面Autovalue生成的代碼很像。
Object-C
Object-C中可以過直接申明@property方式,然后就可以自動(dòng)實(shí)現(xiàn)setter和getter方法,如果要實(shí)現(xiàn)Immutable type方式,需要注明readonly。
hash、equals、description如果使用APPCode,代碼是可以自動(dòng)生成的。
@interfaceOcUser:NSObject@property(readonly)intid;@property(retain,readonly)NSString*name;- (instancetype)initWithId:(int)idname:(NSString*)name;- (NSString*)description;- (BOOL)isEqual:(id)other;- (BOOL)isEqualToUser:(OcUser *)user;- (NSUInteger)hash;@end// ==========================#import"OcUser.h"@implementationOcUser{}- (instancetype)initWithId:(int)idname:(NSString*)name {self= [superinit];if(self) {? ? ? ? _id=id;? ? ? ? _name = name;? ? }returnself;}- (BOOL)isEqual:(id)other {if(other ==self)returnYES;if(!other || ![[other class] isEqual:[selfclass]])returnNO;return[selfisEqualToUser:other];}- (BOOL)isEqualToUser:(OcUser *)user {if(self== user)returnYES;if(user ==nil)returnNO;if(self.id!= user.id)returnNO;return!(self.name!= user.name&& ![self.nameisEqualToString:user.name]);}- (NSUInteger)hash {? ? NSUInteger hash = (NSUInteger)self.id;? ? hash = hash *31u + [self.namehash];returnhash;}- (NSString*)description {NSMutableString*description = [NSMutableStringstringWithFormat:@"<%@: ", NSStringFromClass([selfclass])];? ? [description appendFormat:@"self.id=%i",self.id];? ? [description appendFormat:@", self.name=%@",self.name];? ? [description appendString:@">"];returndescription;}@end
測(cè)試用例
#import#import"OcUser.h"intmain(intargc,constchar*argv[]) {? ? @autoreleasepool {? ? ? ? OcUser *user = [[OcUser alloc] initWithId:100name:@"test"];NSLog(@"user = %@", user);? ? }return0;}// 運(yùn)行結(jié)果// user =
總結(jié)
本文主要介紹了Autovalue的主要用法,以及AutoValu周邊只是,可能說的比較多,比較雜,而且有的地方也不夠深入,但是個(gè)人覺的這是一種思路,一種解決方案,后面如果自己需要造輪子的時(shí)候,我們是可以借鑒的。
本示例代碼地址AutoValueDemo
參考連接
版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。