`
iaiai
  • 浏览: 2203286 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Dart高级(一)——泛型与Json To Bean

阅读更多
从 Flutter 发布到现在, 越来越多人开始尝试使用 Dart 来完成部分功能;

Dart 的前生今世一类的话题,这里就不展开了,只要知道 Flutter 是 google 推出跨平台方案就好,至少不必担心Dart性能与生态问题(如果google真的想支持的话).
先看一下最终的效果显示:


Dart 语言

根据最新的语言排行榜...不好意思,没找到Dart,就目前来看,在前端没拼过JS,在其他领域好像也就目前 Flutter 在支持,不过相信随着Flutter盛行,情况会越来越好的.

Dart 语言有两种模式:JIT,AOT;这个和之前 Android/Java 情况很相似,JVM 一直采用解释执行的方式, 直到 ART 出现,Android端才开始 AOT 模式;

JIT比较方便,不需要提前编译,AOT 运行时比较快,而 Flutter 则在 debug 和 release 时,分别适用不同模式,保证开发效率以及应用流畅.

说了那么多,还没有到正题,接下来,我们来看下 Dart 中比较 "特殊" 的特性:泛型

Dart泛型 与 其他泛型

首先来看一段关于泛型的 Dart 代码(排列组合:C^2_5):
  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泛型的探讨只进行到这里,大概总结一下:
  • 泛型在运行时保留
  • List泛型之间存在关系,可以通过 is 进行判断,如 field is List<num>
  • List泛型即便toString方法返回相同的值,也可能是两个不同的Type

有了上面的论断,接下来进入正题

dart-bean

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,就需要其他的一些技巧.

flutter dart-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;

效果大概是这样:

使用方法很简单,只要按照教程导入包,然后安装插件就好,这里进行几点说明:

1. 插件功能

因为插件修改自GsonFormat,因此命名规范等都沿用了之前的定义,功能主要包括:

[list]
  • 在dart文件中,使用快捷键alt + d,会根据粘贴的json  生成 bean
  • json 支持创建时修改类型,这点和 GsonFormat 相同
  • json 支持嵌套内部类,如果内部类不想创建新的,想使用已存在的某个class,也可以通过创建时修改类型来达到目的:
  • 假如默认要生成的类型是这样:

    我们可以修改类型或者去掉勾选,变成这样:
  • 支持两层List连续嵌套,比如这种情况,会生成List<List>类型的变量:
  • {
        "values":[
            [
            1,2,4
            ]
        ]
    }
    

    [/list]
    2. 库特点

    通过该插件简单的生成较短的代码进行查看:
    //******************************************************************
    //**************************** 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中,直接调用即可.
    分享到:
    评论

    相关推荐

      【Flutter】Dart 泛型 ( 泛型类 泛型方法 特定类型约束的泛型 ).zip

      【Flutter】Dart 泛型 ( 泛型类 | 泛型方法 | 特定类型约束的泛型 ) https://hanshuliang.blog.csdn.net/article/details/114059611 博客源码快照

      JsonToDart-JSON-To-Dart-2.16

      `JsonToDart`是一个针对Flutter开发者的插件,它能够帮助开发者快速、方便地将JSON结构转换为Dart语言的Model类,从而简化数据绑定和序列化的过程。 `JsonToDart`插件的工作原理是解析JSON字符串,并自动生成对应的...

      JsonToDart_windows.zip

      标题“JsonToDart_windows.zip”表明这是一个针对Windows平台的软件工具,主要功能是将JSON数据转换为Dart编程语言的代码。Dart是Google开发的一种面向对象的、高效的编程语言,尤其适用于构建Web和移动应用程序,...

      Dart中的泛型 泛型方法 泛型类 泛型接口、库 自定义库、系统库、第三方库.zip

      在Dart编程语言中,泛型是一种强大的工具,它允许我们编写更加灵活和可复用的代码。泛型涉及到泛型方法、泛型类、泛型接口以及库的使用,这些概念是理解Dart中类型系统的关键部分。下面将详细阐述这些知识点。 1. *...

      Dart2Json插件.zip

      `JsonToDartClass`是将JSON对象转换为Dart类的工具,而`JsonToDart (JSON To Dart)`可能是实现这一功能的命令行工具或者一个IDE插件。 使用Dart2Json插件,你可以将JSON对象输入到工具中,它会自动生成相应的Dart类...

      jsontodart:json生成dart文件

      `jsontodart`是一个实用工具,能够帮助开发者将JSON对象转换为Dart语言的数据模型类,从而简化数据序列化和反序列化的流程。这个工具尤其对那些需要频繁与服务器进行数据交互的应用来说,提供了极大的便利。 1. **...

      json_to_dart:从json字符串生成dart类的库

      var dartClass = jsonToDart(jsonString); print(dartClass); } ``` 运行这段代码后,`json_to_dart`会生成如下Dart类: ```dart class JsonExample { String name; int age; String city; JsonExample({...

      JsonToDart:将json转换为Dart代码的工具,支持Windows,Mac,Web

      该工具将json转换为Dart代码,支持Windows,Mac,Web。 语言:英语| 可空 本地化 编辑课程信息 产生 下载 Windows版Flutter 适用于Macos的Flutter Web Flutter Microsoft Store该功能不是,将来会替换为Flutter...

      json转dart工具.exe

      支持json转dart bean对象,支持嵌套json对象。

      Dart语言中文教程.pdf

      #### 一、Dart简介与特性 **Dart语言**是由Google设计的一款面向对象的编程语言,旨在提供高效、简洁且安全的编程体验。它支持多种编程范式,如面向对象、命令式以及函数式编程。Dart语言的主要特点包括: - **...

      json2dart-converter:Android Studio插件,用于将json转换为dart类

      json2dart-converter 该插件允许您从原始json生成dart类。 只需右键单击目标包,选择“新建”,然后选择“将json转换为dart”。 链接到jetbrains存储库: ://plugins.jetbrains.com/plugin/11460-json2dart

      Json2dart_web:用于将json数据转换为适用于mc包的dart模型的网站

      这个网站提供的服务是将JSON字符串转换为Dart代码,生成的代码可以直接在Dart项目中使用,帮助开发者快速构建与JSON数据交互的模型类。通过这个工具,开发者可以节省手动编写模型类的时间,提高开发效率。 【标签】...

      Flutter开发者json转model_json2dart-1.1.5.zip

      这个名为"Flutter开发者json转model_json2dart-1.1.5.zip"的压缩包提供了一个Dart插件,它帮助开发者快速将JSON格式的数据转换为Dart中的Model类,极大地简化了开发流程。 标题中的“Flutter开发者json转model”指...

      dart-json-mapper:从JSON序列化反序列化Dart对象

      Dart是一种由Google开发的面向对象、现代且类型安全的编程语言,广泛用于构建Web、桌面以及移动应用程序...对于需要频繁与JSON数据交互的Flutter或Dart项目,这是一个值得考虑的库,它能够提高开发效率并保持代码整洁。

      dotnet-功能最全面的Json转换Dart的工具

      压缩包中的文件名“fluttercandies-JsonToDart-fd900cb”可能表示这是一个与Flutter相关的项目,因为“fluttercandies”通常是Flutter开源库或项目的命名模式。Flutter是Google的一个开源UI工具包,用于构建高性能、...

      json-to-dart-null-safety:json到dart空安全性

      这是一个用引导的项目。 入门 首先,运行开发服务器: npm run dev # or yarn dev 用浏览器打开以查看结果。 您可以通过修改pages/index.js来开始编辑页面。 页面在您编辑文件时自动更新。 可以在上访问。 可以...

      cpp-Dart一个高性能网络优化的JSON操作C库

      在现代互联网应用中,JSON作为一种轻量级的数据交换格式,被广泛用于服务器与客户端之间的数据传输。cpp-Dart库的出现,为开发者提供了在C/C++环境中处理JSON数据的强大工具,它在性能和网络效率上做了专门的优化,...

      json_mapper:Dart 的简单轻量级 JSON 映射实用程序

      JSON 映射器一个简单的轻量级 JSON 映射实用程序。我如何使用它? 通过 Dart Pub 包管理器下载依赖项。 将以下行添加到您的 pubspec.yaml 依赖项中: json_mapper: "any"完成后,使用pub get获取包,然后导入它: ...

      jsonmodel一行命令通过Json文件生成DartModel类

      `json_model`库提供了一个简洁高效的方法来实现这一目标,只需一行命令,就可以自动生成对应的Dart Model类。这对于减少手动编写代码的时间和提高开发效率具有重要意义。 首先,我们来了解一下`json_model`。这是一...

      json_serializable.dart:生成实用程序以帮助从JSON序列化到

      在Dart编程语言中,`json_serializable`库是一个非常重要的工具,它为开发者提供了方便的方式来处理JSON序列化和反序列化的过程。这个库的主要目标是简化数据模型类与JSON对象之间的转换,使得代码更加整洁、高效且...

    Global site tag (gtag.js) - Google Analytics