從 Flutter
發佈到如今, 愈來愈多人開始嘗試使用 Dart
來完成部分功能;前端
Dart
的前生今世一類的話題,這裏就不展開了,只要知道 Flutter
是 google
推出跨平臺方案就好,至少沒必要擔憂Dart性能
與生態問題
(若是google
真的想支持的話).java
先看一下最終的效果顯示:git
根據最新的語言排行榜...很差意思,沒找到Dart,就目前來看,在前端沒拼過JS,在其餘領域好像也就目前 Flutter 在支持,不過相信隨着Flutter盛行,狀況會愈來愈好的.github
Dart 語言有兩種模式:JIT
,AOT
;這個和以前 Android/Java 狀況很類似,JVM 一直採用解釋執行的方式, 直到 ART
出現,Android端纔開始 AOT
模式;json
JIT
比較方便,不須要提早編譯,AOT
運行時比較快,而 Flutter 則在 debug 和 release 時,分別適用不一樣模式,保證開發效率以及應用流暢.數組
說了那麼多,尚未到正題,接下來,咱們來看下 Dart
中比較 "特殊" 的特性:泛型
markdown
首先來看一段關於泛型的 Dart
代碼(排列組合:):ide
List<int> a = <int>[];
List<num> b = <num>[];
List<dynamic> c = <dynamic>[];
List<Object> d = <Object>[];
List e = [];
print(a.runtimeType == b.runtimeType); // false
print(a.runtimeType == c.runtimeType); // false
print(a.runtimeType == d.runtimeType); // false
print(a.runtimeType == e.runtimeType); // false
print(b.runtimeType == c.runtimeType); // false
print(b.runtimeType == d.runtimeType); // false
print(b.runtimeType == e.runtimeType); // false
print(c.runtimeType == d.runtimeType); // false
print(c.runtimeType == e.runtimeType); // true
print(d.runtimeType == e.runtimeType); // false
複製代碼
雖然都是 List
類型,但泛型不一樣,獲取到的runtimeType
都不一樣,由此可知:函數
Dart中泛型運行時依舊存在.工具
相比之下,Java中的泛型就比較隨意了:
List<Integer> a = new ArrayList<>();
List<Object> b = new ArrayList<>();
List c = new ArrayList<>();
複製代碼
java泛型在運行時,會被擦除,因此上面的a,b,c判斷時都屬於List
類型.
再回到前面 Dart 部分,能夠看到只有變量c
和e
的運行時類型相同,而且若是使用編譯器的話,就能夠發現:
List c = <dynamic>[];
複製代碼
List<dynamic>
其實就是 List
,二者是同樣的.
在知道了dart的泛型特性後,不由會思考:List<int>
和List
,或者說和List<dynamic>
是什麼關係?和List呢?
仍是開始的dart示例
,在後面咱們添加幾行判斷(變量b的類型爲List\<num\>
)
print(b is List<int>); // false
print(b is List<num>); // true
print(b is List<Comparable>); // true
print(b is List); // true
複製代碼
Comparable
是num類實現的抽象類
,根據驗證結果可知:
dart泛型List時,泛型爲父類的List是泛型爲子類List的父類
這個有點繞,理解這個意思就行;其實看到這裏,若是以前有過java開發經驗的話,就會發現這個其實和java中的數組很類似,參考Java中的數組和List集合以及類型強轉
這個圖說明了java中數組之間是存在繼承關係的,而且和實際類之間的繼承關係類似.
接下來咱們看一下泛型類中泛型的關係;
void main() {
var aa = AA.name("123");
aa.test();
}
class AA<T> {
T field;
AA.name(this.field);
void test() {
print("".runtimeType); //String
print(T); //String
print(field.runtimeType == T); //true
}
}
複製代碼
相信你們對這段代碼以及結果都沒有什麼疑問,泛型在傳遞以後,到了類AA
中仍然爲String
類型,但下面這段代碼就不一樣了:
void main() {
var aa = AA.name([]);
aa.test();
}
class AA<T> {
T field;
AA.name(this.field);
void test() {
print([].runtimeType); //List<dynamic>
print(T); //List<dynamic>
print(field.runtimeType == T); //false
}
}
複製代碼
咱們在建立類時,指定泛型爲List
(或者說是List<dynamic>
),而後在AA
中判斷,發現泛型T
和field的運行時類型
不一樣,雖然toString
是同樣的,但若是打印hashCode
能夠發現,對應着兩個不一樣的Type
.
加入咱們傳入一個map
類型,結果更是不如人意
void main() {
var aa = AA.name({"ss":1});
aa.test();
}
class AA<T> {
T field;
AA.name(this.field);
void test() {
print(field.runtimeType); // _InternalLinkedHashMap<String, int>
print(T); // Map<String, int>
print(field.runtimeType == T); // false
}
}
複製代碼
實際類型是泛型的一個實現類;
那假如傳入一個自定義泛型類呢?
void main() {
var aa = AA.name(BB<num>());
aa.test();
}
class AA<T> {
T field;
AA.name(this.field);
void test() {
print(field.runtimeType); // BB<num>
print(T); // BB<num>
print(field.runtimeType == T); // true
}
}
class BB<T> {}
複製代碼
還好,自定義泛型類時,運行時runtimeType
與泛型Type
是相同的
關於dart泛型的探討只進行到這裏,大概總結一下:
運行時保留
is
進行判斷,如 field is List<num>
toString
方法返回相同的值,也多是兩個不一樣的Type
有了上面的論斷,接下來進入正題
bean只是咱們通用的一種class類的說明,用於代表數據模型.
這裏仍是先看一下其餘語言中的bean;
先看java中一般的作法:
public class TestBean {
private String username;
private boolean isVip;
public String getUsername() {
return username;
}
public TestBean setUsername(String username) {
this.username = username;
return this;
}
public boolean isVip() {
return isVip;
}
public TestBean setVip(boolean vip) {
isVip = vip;
return this;
}
}
複製代碼
kotlin中表示形式會簡單的多:
data class TestBean(
var username: String? = null,
var isVip: Boolean = false
)
複製代碼
其實bean的代碼提現不是關鍵,重要的地方在於,如何方便的爲bean賦值
和操做bean
,由於 JVM 語言能夠進行反射,所以,在獲取到後臺傳入的json格式數據時,咱們能夠很方便的經過一些庫完成自動賦值操做:
var bean = Gson().fromJson("{\"username\":\"www\",\"isVip\":false}",TestBean::class.java)
複製代碼
在dart中,bean的代碼表現形式和其餘語言基本相同,都是在單獨的class中聲明成員變量:
class TestBean {
String username;
bool isVip;
}
複製代碼
若是隻是單純的dart項目,咱們仍然能夠經過鏡像
功能來爲bean賦值:
import 'dart:mirrors';
class TestBean {
String username;
bool isVip;
}
void main() {
Map<String, dynamic> json = {
"username": "www",
"isVip": false,
};
var class_bean = reflectClass(TestBean);
var obj = class_bean.newInstance(Symbol.empty, []).reflectee;
var instance_bean = reflect(obj);
class_bean.declarations.forEach((key, value) {
if (value is VariableMirror) {
var key_string = RegExp("^Symbol\\(\"(.+?)\"\\)\$")
.firstMatch(key.toString())
.group(1);
instance_bean.setField(key, json[key_string]);
}
});
print("${obj.username} ${obj.isVip}"); // www false
}
複製代碼
雖然dart的鏡像功能看起來非常"不爽",但確實是有這個實現的.
不過有些不幸的是,在Flutter中,dart不容許使用鏡像功能,具體緣由能夠參考Flutter實戰中提到一段話:
不少人可能會問Flutter中有沒有像Java開發中的Gson/Jackson同樣的Json序列化類庫?答案是沒有!由於這樣的庫須要使用運行時反射,這在Flutter中是禁用的。運行時反射會干擾Dart的tree shaking,使用tree shaking,能夠在release版中「去除」未使用的代碼,這能夠顯著優化應用程序的大小。因爲反射會默認應用到全部代碼,所以tree shaking會很難工做,由於在啓用反射時很難知道哪些代碼未被使用,所以冗餘代碼很難剝離,因此Flutter中禁用了Dart的反射功能,而正因如此也就沒法實現動態轉化Model的功能。
所以,若是想在Flutter中實現bean,就須要其餘的一些技巧.
目前大多數狀況下,都是在bean類中添加命名構造函數,而後經過工具自動生成部分代碼幫助解析和構建bean.
例如上面的bean類會對應生成以下代碼:
class Response {
String username;
bool isVip;
Response.fromJsonMap(Map<String, dynamic> map)
: username = map["username"],
isVip = map["isVip"];
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['username'] = username;
data['isVip'] = isVip;
return data;
}
}
複製代碼
即多添加兩部份內容:
Response.fromJsonMap
,參數爲Map<String, dynamic>
類型,用於由 json 生成 bean
;爲json反序列化toJson
方法,用於將對象序列化爲json字符串經過添加兩個方法,能夠實現json串及bean的相互轉換.不過若是每次都得手寫那會很麻煩,由於可能須要的字段特別多,通常這種狀況都須要用工具來完成.
官方也提供了相應的庫:json_serializable
,不過若是咱們習慣了 GsonFormat 的快捷,難免會對這種方法感到不滿,而且目前針對 dart 生成bean有不少不得不考慮的問題,好比泛型,嵌套內部類等等,這些狀況下,咱們沒法經過通常的命令行或者工具直接生成.
這裏我改寫了 ,把其中的java類型部分修改成了dart類型,同時修改了生成方法:
插件地址在jetbrains-plugins-JsonToDartBean;
Package包地址在json_holder_impl;
詳細的使用教程請參考github:json-to-dart-bean;
效果大概是這樣:
使用方法很簡單,只要按照教程導入包,而後安裝插件就好,這裏進行幾點說明:
由於插件修改自GsonFormat
,所以命名規範等都沿用了以前的定義,功能主要包括:
在dart文件中,使用快捷鍵alt + d
,會根據粘貼的json 生成 bean
json 支持建立時修改類型,這點和 GsonFormat 相同
json 支持嵌套內部類,若是內部類不想建立新的,想使用已存在的某個class,也能夠經過建立時修改類型來達到目的:
假如默認要生成的類型是這樣:
咱們能夠修改類型或者去掉勾選,變成這樣:
支持兩層List連續嵌套,好比這種狀況,會生成List<List>類型的變量:
{
"values":[
[
1,2,4
]
]
}
複製代碼
經過該插件簡單的生成較短的代碼進行查看:
//******************************************************************
//**************************** Generate By JsonToDartBean **********
//**************************** Thu Jun 06 18:33:38 CST 2019 **********
//******************************************************************
import 'package:json_holder_impl/json_holder_impl.dart';
class FirstBean with JsonHolderImpl<FirstBean> {
/// [key : value] => [name : www]
String get name => getValue("name");
set name(String value) => setValue("name", value);
FirstBean.fromJson([Map<String, dynamic> json]) {
fromJson(json);
}
@override
JsonHolderImpl<FirstBean> provideCreator(Map<String, dynamic> json) {
return FirstBean.fromJson(json);
}
@override
List<FirstBean> provideListCreator() {
return <FirstBean>[];
}
@override
List<List<FirstBean>> provideListListCreator() {
return <List<FirstBean>>[];
}
}
複製代碼
能夠看到類中其實沒有生成任何的成員域,全部變量都同等的被get 和 set
方法替代,這種方式相對於目前廣泛的bean構建方法有一個好處,那就是變量值只有在真正調用的時候纔會去解析,能在必定程度上加快運行速度(一樣在父類中處理了類型轉換,而且可自定義,具體自定義方式參考上面提到的github教程便可).
代碼中只看到了fromJson
方法,toJson
方法 定義在了父類JsonHolderImpl
中,直接調用便可.
注: 代碼不可用於商業用途 注: 若有 插件 GsonFormat 涉及到的版權問題,請及時告知