【dart学习】-- Dart之类和对象
一,概述
类(Class)是面向对象程序设计,实现信息封装的基础。类是一种用户定义的类型。每个类包含数据说明和一组操作数据或传递消息的函数。类的实例称为对象。
Dart的类与其它语言都有很大的区别,比如在dart的类中可以有无数个构造函数,可以重写类中的操作符,有默认的构造函数,由于dart没有接口,所以dart的类也是接口,因此你可以将类作为接口来重新实现。
Dart是一门使用类和单继承的面向对象语言所有的对象都是类的实例,并且所有的类都是Object
的子类。
二,类定义
- 类的定义用
class
关键字 - 如果未显式定义构造函数,会默认一个空的构造函数
类首字母必须大写
- 使用
new
关键字和构造函数
来创建对象 class Person { //未定义父类的时候,默认继承自Object
num x;
num y;
num z;
} void main(List<String> args){
var person = new Person();//调用默认的构造函数
person.x = ; //使用点(.
)引用实例变量或方法
person.y = ;
person?.z = ; //如果p不为空,设置它的变量y的值为4
print(person.x);
print(person.y);
print(person.z);
}结果:
三, 实例变量
- 声明实例变量时,所有未初始化的实例变量的值为null
- 对象的成员包括函数和数据(分别是方法和实例变量)。使用点(
.
)引用实例变量或方法; - 使用
?.
来确认前操作数不为空, 常用来替代.
, 避免左边操作数为null引发异常 void main(){
var point = new Point();
point.x = ; //使用点(.
)引用实例变量或方法
point?.y = 5;//如果p不为空,设置它的变量y的值为5
print(point.x); print(point.y);
}class Point {
int x; // null
int y; // null
int z = ; //
}
四,构造函数
- 如果你没有声明构造函数,默认有构造函数,默认构造函数没有参数,调用父类的无参构造函数。子类不能继承父类的构造函数
class Person {
int x;
int y;
} void main(List<String> args){
var person = new Person();
} - 构造函数就是一个与类同名的函数,关键字 this 是指当前的,只有在命名冲突时有效,否则dart会忽略处理
void main(){
var point = new Point(, );
}class Point {
int x;
int y;
//自己定义的类名构造函数
Point(int x, int y) {
this.x = x;
this.y = y;
}
}在Dart中构造函数的名称可以是类名
ClassName
或者类名和标识符ClassName.identifier
。 其中构造函数名称是“ClassName”的函数叫“类名构造函数”;构造函数名称是“ClassName.identifier”的函数叫“命名构造函数”。(1)类名构造函数 (ClassName)
import 'dart:math'; class Point {
int y;
int x; // 类名构造函数
Point(num x, num y) {
this.x = x;
this.y = y;
}
// .....
}在构造函数里初始化成员属性是很常见的事情,因此Dart开发了新的语法糖来简化这种操作,比如将Point的类名构造构造函数改写成
class Point {
num x, y;
// 注意x,y的赋值会在构造函数执行之前完成.
Point(this.x, this.y);
}(2)命名构造函数(ClassName.identifie)
使用命名构造函数可以为类提供多个构造函数,按官方的说法就是提供额外的清晰度class Point {
num x, y; Point(this.x, this.y); // 命名构造函数
Point.origin() {
x = ;
y = ;
}
}调用命名构造函数
main(List<String> args) {
// 调用命名构造函数
Point point1 = Point.origin();
}
在命名构造函数里也可以用新的语法糖来简化这种操作,比如将Point的类名构造构造函数改写成
class Point {
num x, y;// 注意x,y的赋值会在构造函数执行之前完成.
Point(this.x, this.y);
}
(2)命名构造函数(ClassName.identifie)
使用命名构造函数可以为类提供多个构造函数,按官方的说法就是提供额外的清晰度
class Point {
num x, y; Point(this.x, this.y); // 命名构造函数
Point.origin() {
x = ;
y = ;
}
}调用命名构造函数
main(List<String> args) {
// 调用命名构造函数
Point point1 = Point.origin();
}在命名构造函数里也可以用新的语法糖来简化这种操作,比如将Point的类名构造构造函数改写成
class Point {
num x, y;
//类名构造函数
Point(this.x, this.y);
// 命名构造函数
Point.origin(this.x,this.y);
}void main(List<String> args){
var point = new Point.Orgin(,);
print(point.x);
print(point.y);
}(3)默认构造函数(前面我我们已经说了,我们放在这里再提一下,方便区分)
如果类中没有声明构造函数,Dart会提供一个默认的构造函数。这个默认的构造函数会调用父类的默认构造函数,并且该构造函数是没有参数的。class Person {
int x;
int y;
} void main(List<String> args){
var person = new Person();
}Dart的第一个版本实例化对象需要new关键字,但在Dart 2之后就去掉了new关键字
main(List<String> args) {
// 调用类名构造函数
Point point1 = Point(,);
print(point1.x);
}调用父类非默认的构造函数(类比下面的重定向理解记忆)
在默认情况下,子类可以调用父类的未命名,无参数的构造函数即默认构造函数。父类的构造函数会在子类的构造函数之前开始调用,如果子类中存在需要初始化的成员属性,则可以先初始化子类成员属性,再调用父类的构造函数,执行过程如下
- 初始化子类成员属性
- 调用父类构造函数
- 子类构造函数
如果父类中没有默认的构造函数,你必须手动调用父类的构造函数,在子类的构造函数体之前通过
:
指定调用父类构造函数,示例如下// Person类中没有一个无参数,未命名的构造函数
class Person {
String firstName;
// 命名构造函数
Person.fromJson(Map data) {
print('in Person');
}
} class Employee extends Person {
// 你必须调用父类的super.fromJson(data).
Employee.fromJson(Map data) : super.fromJson(data) {
print('in Employee');
}
} main() {
var emp = new Employee.fromJson({});
}
重定向构造函数 (在这里 :有重新指向的含义)
有时构造函数的唯一目的是重定向到同一类中的另一个构造函数。重定向构造函数的主体为空,构造函数调用出现在冒号(
:
)之后 。 大意就是在创建类时,我定义一个命名构造函数,但是这个构造函式的主体我不实现。直接通过:另外一个构造函数。实现对外界传入的参数接收并赋值给内部的变量。class Point {
num x, y;
//类名构造函数
Point(this.x, this.y);
// 命名构造函数
Point.order(this.x,this.y);
Point.origin(num a,num b):this.order(a,b); //重定向构造函数, origin构造函数将外界的传值,指向给了order构造函数。
}
void main(List<String> args){
var point = new Point.origin(,);
print(point.x);
print(point.y);
}- 常量构造函数
如果你的类创建的对象从不改变,你可以创建一些编译时的常量对象。因此,定义一个const
构造函数,且保证所有的对象变量都是final。class ImmutablePoint {
static final ImmutablePoint origin = const ImmutablePoint(, );
final num x, y;
const ImmutablePoint(this.x, this.y);
} 工厂构造函数
在实现一个构造函数时使用
factory
关键字,该构造函数并不总是创建其类的新实例。例如,工厂构造函数可能会从缓存中返回实例,也可能会返回子类型的实例。class Logger {
final String name;
bool mute = false; // _cache是私有变量
//_在名称前,表示该变量为私有
static final Map<String, Logger> _cache = <String, Logger>{}; factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = Logger._internal(name);
_cache[name] = logger;
return logger;
}
} Logger._internal(this.name);void log(String msg) {
if (!mute) print(msg);
}
}注意:工厂构造者对this没有访问权限。
像调用任何其他构造函数一样调用工厂构造函数:
var logger = Logger('UI');
logger.log('Button clicked');
五,方法
方法是为对象提供行为的函数。
- Getters和Setters 方法
(1)Getters和setters是读取和修改对象的特定方法,每次调用对象的属性时,Dart都会隐式的调用一次getter方法,这允许你可以在修改或者读取对象属时做一些操作。
(2)通过get
和set
关键词重写对象的默认行为。class Rectangle {
num left, top, width, height;
//类名构造函数
Rectangle(this.left, this.top, this.width, this.height);
// 重写right属性(类比oc记忆,这里多了一个set和get的关键字)
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
} void main() {
var rect = Rectangle(, , , );
assert(rect.left == );
rect.right = ;
assert(rect.left == -);
} 实例方法
在对象的实例方法有权限获取对象变量和this
,在接下来的例子里distanceTo
就是一个对象方法:import 'dart:math';
class Point {
num x, y;
Point(this.x, this.y);
num distanceTo(Point other) {
var dx = x - other.x;
var dy = y - other.y;
return sqrt(dx * dx + dy * dy);
}
}抽象方法
(1) 实例的getter和setter方法就是抽象的方法,定义一个接口,但将其实现留给其他类。
(2)抽象方法使用分号 ; 而不是方法体
(3)抽象方法只存在于抽象类中。abstract class Doer {
//...定义实例变量和方法... //定义一个抽象方法
void doSomething();
} class EffectiveDoer extends Doer {
void doSomething() {
//...实现一个抽象方法...
}
}
六,抽象类和接口
抽象类
使用abstract
修改器可以定义一个抽象类。抽象类是不能被实例化的,但对于定义接口是非常有用的,如果你想实例化抽象类,你必须实现抽象类,才能被实例化
// 此对象爱你过是抽象类,因此不能被实例化
abstract class AbstractContainer {
// 定义构造函数、字段、方法... void updateChildren(); // 抽象方法
}隐式的接口
每个类都是都是隐式的接口,包括类的方法和属性。如果你想创建一个类A不继承B的实现,可以实现B的接口来创建类A。一个类允许通过implements
关键词可以实现多个接口// 每个类都是一个隐式的接口,所以Person类也是个接口,包括成员属性和方法.
class Person {
// 可在接口中实现, 但仅对这个库可见.
final _name; // 构造函数不能够被接口实现
Person(this._name); // 可在接口中实现.
String greet(String who) => 'Hello, $who. I am $_name.';
} // 实现Person接口.
class Impostor implements Person {
get _name => ''; String greet(String who) => 'Hi $who. Do you know who I am?';
} String greetBob(Person person) => person.greet('Bob'); void main() {
print(greetBob(Person('Kathy')));
print(greetBob(Impostor()));
}实现多个接口
class Point implements Comparable, Location {...}
七,类的继承
- 使用extends创建子类,super引用父类,子类可以重写实例方法、getter和setter,使用@override注释重写,使用@proxy注释来忽略警告
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
} class SmartTelevision extends Television {
void turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
} 重写成员
可以使用
@override
关键字,子类可以重写实例的方法,getters和settersclass SmartTelevision extends Television {
@override
void turnOn() {...}
// ···
}重写操作符
你可以重写以下表中显示的运算符。例如,如果定义Vecor类,则可以定义
+
方法来添加两个vectors。
注意:
你可能已经注意到了!=
不是可重写的运算符。表达式E1!= E2
只是语法上的糖!( E1 = = E2 )
< + | []
> / ^ []=
<= ~/ & ~
>= * << ==
– %可以重写操作符,下面是重写加减操作符的示例
class Vector {
final int x, y; Vector(this.x, this.y); Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
Vector operator -(Vector v) => Vector(x - v.x, y - v.y); // Operator == and hashCode not shown. For details, see note below.
// ···
} void main() {
final v = Vector(, );
final w = Vector(, ); assert(v + w == Vector(, ));
assert(v - w == Vector(, ));
}如果你需要重写
==
操作符,请参考操作符教程
noSuchMethod()
当用户调用你定义的类中不存在的属性与方法时,可以做出一些响应,通过重写noSuchMethod()
class A {
@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ' +
'${invocation.memberName}');
}
}
八,类变量和类方法
使用static
关键字实现类范围的变量和方法。
- 静态变量
静态变量(类变量)对于类范围的状态和常量非常有用:
静态变量在使用之前不会初始化。class Queue {
static const initialCapacity = ;
// ···
} void main() {
assert(Queue.initialCapacity == );
} - 静态方法
静态方法(类方法)不能在实例操作,因此它没有访问this的权限。
import 'dart:math'; class Point {
num x, y;
Point(this.x, this.y);
static num distanceBetween(Point a, Point b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
return sqrt(dx * dx + dy * dy);
}
} void main() {
var a = Point(, );
var b = Point(, );
var distance = Point.distanceBetween(a, b);
assert(2.8 < distance && distance < 2.9);
print(distance);
}注意:
(1)为了常用或广泛使用的实用程序和功能,考虑使用顶层函数,而不是静态方法。import 'dart:math'; class Point {
num x, y;
Point(this.x, this.y);
}
// 顶级函数
num distanceBetween(Point a, Point b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
return sqrt(dx * dx + dy * dy);
} void main() {
var a = Point(, );
var b = Point(, );
var distance = distanceBetween(a, b);
assert(2.8 < distance && distance < 2.9);
print(distance);
}(2)可以使用静态方法作为编译时常量。例如,可以将静态方法作为参数传递给常量构造函数。
九,访问控制
- 默认类中的所有属性和方法是public的。在dart中,可以在属性和方法名前添加“_”使私有化。现在让我们使name属性私有化。
main(List<String> args) {
Dog d = new Dog('Duffy', );
print(d.name); //This will throw error
} class Dog {
String _name; //私有的变量
int age; //公有变量//类名构造函数
Dog(this.name, this.age);//setter && getter 方法
String get respectedName {
return 'Mr.$name';
}set respectedName(String newName) {
name = newName;
}//命名构造函数
Dog.newBorn() {
name = 'Doggy';
age = ;
}//公有实例方法
bark() {
print('Bow Wow');
}//私有的实例方法
_hiddenMethod() {
print('I can only be called internally!');
}
}
十,枚举类型
枚举类型,通常称为枚举,是一种特殊类型的类,用于表示固定数量的常量值。
使用枚举
使用enum
关键词来声明一个枚举类型enum Color { red, green, blue }
枚举中的每个值都有一个
index
索引,它返回枚举声明中值的从零开始的位置。例如,第一个值具有索引0,第二个值具有索引1。assert(Color.red.index == );
assert(Color.green.index == );
assert(Color.blue.index == );若要获取枚举中所有值的列表,请使用枚举的
values
常量。List<Color> colors = Color.values;
assert(colors[] == Color.blue);你可以在switch语句中使用枚举,如果不处理枚举的所有值,将会收到警告:
var aColor = Color.blue; switch (aColor) {
case Color.red:
print('Red as roses!');
break;
case Color.green:
print('Green as grass!');
break;
default: // 没有default,将会报错
print(aColor); // 'Color.blue'
}枚举类型有以下限制:
- 你不能子类化、混合或实现枚举。
- 不能显式实例化枚举。
十一,泛型
- 泛型是程序设计语言的一种特性。允许程序员在强类型程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须作出指明。
- 从字面的意思理解来看,泛型,泛就是模糊、暂不确定暂定的意思。可以这样理解,使用泛型就是,定义的一个类型,类型暂不确定,给使用给一个占位符给代替,在使用的时候可以给确定其定义的类型。
- 泛型,基本上强类型语言都支持泛型,比如java,Oc,swift 所以Dart也不例外
- dart全面支持泛型。假设你想在你定义的类中,想持有任意类型的数据。如下是怎样使用泛型定义这样的类。
main(List<String> args) {
DataHolder<String> dataHolder = new DataHolder('Some data');
print(dataHolder.getData());
dataHolder.setData('New Data');
print(dataHolder.getData());
} class DataHolder<T> {
T data; DataHolder(this.data); getData() {
return data;
} setData(data) {
this.data = data;
}
}
【dart学习】-- Dart之类和对象的更多相关文章
- [dart学习]第四篇:函数和操作符(本篇未完待续)
接着学习dart的函数和操作符. 1.函数Function dart是一种真正的面向对象的语言,通常一个函数也是Function类型的对象,这也就是说可以把函数赋值给一个变量,或者作为另一个函数的入参 ...
- [dart学习]第二篇:dart变量介绍 (一)
前言 本文的所有内容均是官方文档的简单翻译和理解,需要查看原文,请登录 https://www.dartlang.org/guides/language/language-tour 阅读, 让我们 ...
- 【dart学习】-- Dart之异步编程
一,概述 编程中的代码执行,通常分为同步与异步两种. 同步:简单说,同步就是按照代码的编写顺序,从上到下依次执行,这也是最简单的我们最常接触的一种形式.但是同步代码的缺点也显而易见,如果其中某一行或几 ...
- 【dart学习】-- Dart之函数
1. 指定返回值得函数 /** * 无返回值的函数 * params: 可以是任意类型(var和Object类型也可以任意类型). 当然这里的参数类型你可以随意指定我这里已dynamic为例 * 参数 ...
- Dart语言学习( 一) 为什么学习Dart?
为什么学习Dart? Google及全球的其他开发者,使用 Dart 开发了一系列高质量. 关键的 iOS.Android 和 web 应用. Dart 非常适合移动和 web 应用的开发. 高效 D ...
- Dart 学习资料
Dart 学习资料: 学习资料 网址 Dart 编程语言中文网 http://dart.goodev.org/ Dart 官方包仓库 https://pub.dartlang.org/ 你想了解的Da ...
- [dart学习]第五篇:操作符
前言:本系列内容假设读者有一定的编程基础,如了解C语言.python等. 本节一起来学习dart的操作符,直接拷贝官网的操作符描述表如下: Description Operator unary pos ...
- Dart学习笔记-运算符-条件表达式-类型转换
Dart学习笔记-运算符-条件表达式-类型转换 一.运算符 1.算术运算符 + (加)- (减)* (乘)/ (除)~/ (取整) %(取余) 2.关系运算符 == (等等) != (不等) > ...
- Dart 学习
语言特性 Dart所有的东西都是对象, 即使是数字numbers.函数function.null也都是对象,所有的对象都继承自Object类. Dart动态类型语言, 尽量给变量定义一个类型,会更安全 ...
- 简单易懂的Dart》 - Dart语言中文简明教程
转自:https://www.blackglory.me/straightforward-dart/ Dart是Google公司发布的网络编程语言,其诞生的目的是为了让广大C类OOP程序员们克服Jav ...
随机推荐
- C# GDI+简单绘图
一.使用Pen画笔 Pen的主要属性有: Color(颜色),DashCap(短划线终点形状),DashStyle(虚线样式),EndCap(线尾形状), StartCap(线头形状),Width(粗 ...
- 【Flutter学习】可滚动组件之ScrollView
一,概述 ScrollView 是一个带有滚动的视图组件. 二,组成部分 ScrollView 由三部分组成: Scrollable - 它监听各种用户手势并实现滚动的交互设计.可滚动Widget都直 ...
- sql server 基础语法4 实践练习+子查询
drop table class create table class ( classId ) primary key not null, cName ) ) insert into class ', ...
- 探索Redis设计与实现7:Redis内部数据结构详解——intset
本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...
- webpack+vue+koa+mongoDB,从零开始搭建一个网站
github 地址 https://github.com/wangxiaoxi... webpakc+vue的搭建1.新建项目文件夹(see-films);2.npm init //初始化项目3.搭建 ...
- CSS入门之盒模型(六分之四)
盒模型要点知识 务必注意看,这可是前端面试 必定会遇到 的问题. box-sizing 盒模型的主要CSS属性,除继承外有两个值: content-box 这里不再细说历史原因,只说其作用. cont ...
- 32. 持续集成简介及JDK、Tomcat、Jenkins环境搭建
持续集成简介 持续集成是一种软件开发实践,即团队开发成员经常集成他们的工作,通常每个成员每天至少集成一次,也就意味着每天可能会发生多次集成.每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验 ...
- PAT_A1100#Mars Numbers
Source: PAT A1100 Mars Numbers (20 分) Description: People on Mars count their numbers with base 13: ...
- log4j日志记录到文件
要写日志信息到一个文件中,必须使用org.apache.log4j.FileAppender.有以下FileAppender的配置参数: FileAppender配置: 属性 描述 immediate ...
- 8种常见SQL错误用法,你中招了吗?
作者:db匠 来源:https://yq.aliyun.com/articles/72501 1.LIMIT 语句 分页查询是最常用的场景之一,但也通常也是最容易出问题的地方.比如对于下面简单的语句, ...