那些你不知道的Dart细节之类的点点滴滴(五)

简介: Dart中的类

前言

前几篇文章分别讲解了Dart中的变量、内置类型、函数(方法)、操作符、流程控制语句和异常,对Dart的基本语法已经有了很多的了解,那么今天来说一下Dart中的类。前几篇文章没有看的,建议先看一下前几篇文章。

那些你不知道的Dart细节之变量

那些你不知道的Dart细节之内置类型

那些你不知道的Dart细节之函数(方法)

那些你不知道的Dart细节之操作符、流程控制语句、异常

那些你不知道的Dart细节之类的点点滴滴

那些你不知道的Dart细节之泛型和库

构造函数

前几篇文章中在讲函数(方法)的一篇中提到过,这里再说一下吧,首先来看一下Java中构造函数的写法:

class Point {
  double x;
  double y;
  Point(int x, int y) {
    this.x = x;
    this.y = y;
  }
}

下面是dart中的建议写法:

class Point {
  num x;
  num y;
  Point(this.x, this.y);
}

命名构造函数

使用命名构造函数可以为一个类实现多个构造函数, 或者使用命名构造函数来更清晰的表明你的意图。

class Point { 
  num x; 
  num y; 
  Point(this.x, this.y); 
  //命名构造函数
  Point.fromJson(Map json) { 
    x = json['x']; 
    y = json['y']; 
  } 
}

重定向构造函数

一个重定向构造函数是没有代码的,在构造函数声明后,使用 冒号调用其他构造函数。

class Point { 
  num x; 
  num y; 
  Point(this.x, this.y); 
  //重定向构造函数,使用冒号调用其他构造函数
  Point.alongXAxis(num x) : this(x, 0);
}

初始化列表

在构造函数体执行之前可以初始化实例参数。 使用逗号分隔初始化表达式。初始化列表非常适合用来设置 final 变量的值。

import 'dart:math';
class Point {
  //final变量不能被修改,必须被构造函数初始化
  final num x;
  final num y;
  final num distanceFromOrigin;
  //初始化列表
  Point(x, y)
      : x = x,
        y = y,
        distanceFromOrigin = sqrt(x * x + y * y);
}

调用超类构造函数

首先来建立一个超类(父类),下面的代码都继承自此类:

class Parent {
  int x;
  int y;
  //父类命名构造函数不会传递  
  Parent.fromJson(x, y)
      : x = x,
        y = y {
    print('父类命名构造函数');
  }
}

超类命名构造函数不会传递,如果希望使用超类中定义的命名构造函数创建子类,则必须在子类中实现该构造函数。

class Child extends Parent {
  int x;
  int y;
  Child.fromJson(x, y) 
  : x = x,
    y = y,
    print('子类命名构造函数');
  }
}

如果超类没有默认构造函数, 则你需要手动的调用超类的其他构造函数,调用超类构造函数的参数无法访问 this。


class Child extends Parent {
  int x;
  int y;
  //若超类没有默认构造函数, 需要手动调用超类其他构造函数
  Child(x, y) : super.fromJson(x, y) {
    //调用父类构造函数的参数无法访问 this
    print('子类构造函数');
  }
}

在构造函数的初始化列表中使用 super(),需要把它放到最后。

class Child extends Parent {
  int x;
  int y;
  Child.fromJson(x, y) 
  : x = x,
    y = y,
    super.fromJson(x, y) {
    print('子类命名构造函数');
  }
}

常量构造函数

定义const构造函数要确保所有实例变量都是final。

const关键字放在构造函数名称之前。

class Point2 {
  //定义const构造函数要确保所有实例变量都是final
  final num x;
  final num y;
  static final Point2 origin = const Point2(0, 0);
  //const关键字放在构造函数名称之前,且不能有函数体
  const Point2(this.x, this.y);
}

工厂构造函数(Dart中的单例)

工厂构造函数是一种构造函数,与普通构造函数不同,工厂函数不会自动生成实例,而是通过代码来决定返回的实例对象。如果一个构造函数并不总是返回一个新的对象(单例),则使用 factory 来定义这个构造函数。工厂构造函数无法访问this。

class Singleton {
  String name;
  //工厂构造函数无法访问this,所以这里要用static
  static Singleton _cache; 
  //工厂方法构造函数,关键字factory
  factory Singleton([String name = 'singleton']) =>
      Singleton._cache ??= Singleton._newObject(name);
  //定义一个命名构造函数用来生产实例
  Singleton._newObject(this.name);
}

Setter和Getter

在Java中get和set方法可以直接生成,在Dart中无需自己定义。每个实例变量都隐含的具有一个 getter, 如果变量不是 final 的则还有一个 setter。可以通过实行 getter 和 setter 来创建新的属性, 使用 get 和 set 关键字定义 getter 和 setter。可以开始使用实例变量,后来可以把实例变量用函数包裹起来,而调用你代码的地方不需要修改。下面是代码实例:

class Rectangle {
  num left;
  num top;
  num width;
  num height;
  Rectangle(this.left, this.top, this.width, this.height);
  num get right => left + width;
  set right(num value) => left = value - width;
  num get bottom => top + height;
  set bottom(num value) => top = value - height;
}

抽象类(接口)

不能被实例化,除非定义一个工厂构造函数。

这个很好理解,在Java中的抽象类也同样不可实例化。

抽象类通常用来定义接口, 以及部分实现。

在Dart中没有interface这个关键字,只有abstract,所以可以使用abstract来生成接口:

abstract class Demo{
  void zhujiang();
}
class Zhu implements Demo{
  @override
  void zhujiang() {}
}

抽象类通常具有抽象方法,抽象方法不需要关键字,以分号结束即可。

其实上面代码也可以用作这个实例,只需要把implements变成extends即可:

class Zhu extends Demo{
  @override
  void zhujiang() {}
}

接口方式使用时,需要重写抽象类的成员变量和方法,包括私有的。

一个类可以implement一个普通类。Dart任何一个类都是接口。

一个类可以implement多个接口。

最后几个说的其实有点绕,说白了就是可以实现多个,这里需要注意的是:implement只是实现接口,你需要重写接口中的方法,不然是不会执行的。还有一点是如果想extents多个类的话需要使用with关键字。说了这么多不如直接看代码,看代码应该好理解一些:

abstract class Demo{
  void zhujiang();
}
abstract class Demo2{
  void zhujiang();
}
abstract class Demo3{
  void zhujiang();
}
class Zhu extends Demo with Demo2,Demo3 implements Demo3,Demo2{
  @override
  void zhujiang() {}
}

可调用类

实现call()方法可以让类像函数一样能够被调用。这个很简单,直接上代码:

class ClassFunction {
  call(String a, String b, String c) => '$a $b $c!';
}
main() {
  var cf = new ClassFunction();
  var out = cf("aaa","flutter","damon");
  print('$out');
  print(cf.runtimeType);
  print(out.runtimeType);
  print(cf is Function);
}

下面是打印结果:

lib/5.dart: Warning: Interpreting this as package URI, 'package:darttest/5.dart'.
aaa flutter damon!
ClassFunction
String
false

Mixin

子类没有重写超类A方法的前提下,如果2个或多个超类拥有相同签名的A方法,那么子类会以继承的最后一个超类中的A方法为准。

如果子类自己重写了A方法则以本身的A方法为准。

在这里先不过多解释Minxin,我的理解就类似于策略模式,抽出不变的为接口,然后多实现。这里先不写了,如果有可能的话等专门写一篇介绍Dart中的Minxin的文章吧。

总结

到这里为止Dart的第五篇文章完成。本篇文章主要讲解了一下Dart语言的类。下一篇文章讲解一下Dart中的泛xing和导入库,希望大家能够喜欢

目录
相关文章
|
16天前
|
安全 编译器 Swift
Swift开发
Swift开发
34 9
|
26天前
|
设计模式 程序员 Serverless
探索编程之美:从代码细节到技术哲学
【10月更文挑战第28天】在这篇文章中,我们将一起走进编程的世界,探索那些隐藏在代码行间的艺术与哲理。通过深入浅出的讲解和实际的代码示例,我们不仅能够学习到技术层面的知识,更能体会到编程作为一种创造性活动所带来的乐趣和启示。无论你是初学者还是资深开发者,都能在这段旅程中找到新的视角和灵感。
45 11
|
4月前
|
开发者 C# Android开发
明白吗?Xamarin与Native的终极对决:究竟哪种开发方式更适合您的项目需求,让我们一探究竟!
【8月更文挑战第31天】随着移动应用开发的普及,开发者面临多种技术选择。本文对比了跨平台解决方案Xamarin与原生开发方式的优势与劣势。Xamarin使用C#进行跨平台开发,代码复用率高,可大幅降低开发成本;但因基于抽象层,可能影响性能。原生开发则充分利用平台特性,提供最佳用户体验,但需维护多套代码库,增加工作量。开发者应根据项目需求、团队技能和预算综合考量,选择最适合的开发方式。
117 0
|
Android开发 开发者 Kotlin
安卓MVI架构真的来了?动手试着封装吧(一)下
安卓MVI架构真的来了?动手试着封装吧(一)
151 1
|
Android开发 开发者 Kotlin
安卓MVI架构真的来了?动手试着封装吧(二)上
安卓MVI架构真的来了?动手试着封装吧(二)
135 0
安卓MVI架构真的来了?动手试着封装吧(二)上
|
JSON 编译器 测试技术
Go 开发十种常犯错误(上)
Go 开发十种常犯错误
50 0
|
测试技术 Go 开发者
Go 开发十种常犯错误(下)
Go 开发十种常犯错误
57 0
|
开发框架 自然语言处理 Java
妈!Jetpack Compose太难学了,别怕,这里帮你理清几个概念(一)
妈!Jetpack Compose太难学了,别怕,这里帮你理清几个概念
360 0
|
API Android开发
妈!Jetpack Compose太难学了,别怕,这里帮你理清几个概念(二)
妈!Jetpack Compose太难学了,别怕,这里帮你理清几个概念
462 0
|
API Android开发 Kotlin
安卓MVI架构真的来了?动手试着封装吧(三)下
安卓MVI架构真的来了?动手试着封装吧(三)
121 0