Dart语言一日游
前言
Dart是谷歌Flutter框架使用的语言。Dart语言目前还是个冷门语言,但是它很好上手,对于有编程基础的人来说,一天时间足够掌握了。本文就是结合我自己的学习过程,给大家总结一下经验,帮助大家快速上手。
学习Dart语言,这一篇文章就够用了。
本文包括哪些知识点
- 如何创建一个类和它的构造器
- 如何定义类的变量
- 如何创建getter和setter
- Dart对于变量私有公有访问的控制
- 如何使用工厂模式创建一个类实例
- 类的继承机制
- Dart函数式编程
需要准备的东西
因为本文只是学习Dart语言,只需要一个能科学上网的浏览器就够用了。
Dart提供了在线的IDE开发环境DartPad
,极大的方便了我们的学习。
万物之源:类和对象
对于一门面向对象的程序语言,类和对象永远都是最重要的组成部分。所以我们首先来看如何创建一个Dart类。
打开DartPad
,发现已经帮你写好了一个HelloWorld程序了。其中main函数就是入口主函数。这个函数循环输出四行字符串。
void main() {
for (var i = 0; i < 4; i++) {
print('hello $i');
}
}
类的创建
下面我们来创建一个类,我们用自行车来距离。创建一个Bicycle类。代码如下:
class Bicycle{
int speed;
String desc;
}
void main() {
}
怎么样,是不是非常熟悉?(注意,这里每行结尾还是要写分号,略蛋疼)
这个类有两个属性,速度和描述。如何创建一个实例呢?也很简单,这样就好了。
class Bicycle{
int speed;
String desc;
}
void main() {
var bike = Bicycle();
bike.speed = 20;
bike.desc = "我的自行车";
print(bike);
}
点一下页面上的run按钮,输出结果:
Instance of 'Bicycle'
这里有两个知识点:
- 我们用
var
关键字创建了一个bike实例并且输出。如果想要创建一个不可变的变量,用关键字final
。(这个final就是kotlin的val) - 类的属性都没有
public
或者private
关键字。没错,Dart没有public
和private
关键字,类里面声明的属性默认都是公共的。这个要注意一下。如何声明私有变量后面再讲。
构造函数
能不能像我们习惯的那样写个构造函数传参呢?当然可以,这样写就行了:
class Bicycle {
int speed;
String desc;
Bicycle(this.speed, this.desc);
}
void main() {
var bike = Bicycle(20, "我的自行车");
print(bike);
}
// 上面的简便构造函数写法与下面的等价:
Bicycle(int speed, String desc) {
this.speed = speed;
this.desc = desc;
}
run一下,输出和刚才还是一样的
Instance of 'Bicycle'
这个输出不太友好,我们来改进一下。同Java一样,每个类有自己的toString()
函数,我们覆写它就可以。
class Bicycle {
int speed;
String desc;
Bicycle(this.speed, this.desc);
@override
String toString() {
return '$desc速度: $speed 公里/小时';
}
// 简便写法
@override
String toString() => '$desc速度: $speed 公里/小时';
}
注意,函数前面不用写fun
关键字。简便的写法中,用=>
符号来确定返回值。最后的输出为:
我的自行车速度: 20 公里/小时
好了下面我们来介绍一下如何声明一个私有的变量。因为没有private
关键字,所以Dart用了一个比较蛋疼的处理,就是变量名前面加下划线,你没看错,是下划线。。。所以我们把desc
变量声明为_desc
,这样就把它私有化了。
class Bicycle {
int speed;
String _desc;
Bicycle(int speed) {
this.speed = speed;
this._desc = "我的自行车";
}
@override
String toString() {
return '$_desc速度: $speed 公里/小时';
}
}
void main() {
var bike = Bicycle(20);
print(bike);
}
这样修改后,在main函数中就无法访问desc变量了,不能set也不能get。
那么问题又来了,如果我想创建一个只读的变量呢?能get但是不能set。这个就需要在私有变量的基础上,自己提供一个get函数了。写法是这样的
class Bicycle {
int speed;
String _desc;
// 为私有变量提供get函数
String get desc => _desc;
Bicycle(int speed) {
this.speed = speed;
this._desc = "我的自行车";
}
@override
String toString() {
return '$_desc速度: $speed 公里/小时';
}
}
void main() {
var bike = Bicycle(20);
print(bike.desc);
print(bike);
}
最后介绍一下为一个类添加函数,这个非常简单,看一下就好:
class Bicycle {
int speed;
String _desc;
void speedUp(int value) {
this.speed += value;
}
int getSpeed() {
return this.speed;
}
}
OK,类和对象这个部分已经完成了。
可选参数
Dart和Kotlin一样,支持函数设置可选参数,避免Java那种累赘的重载。这里我们用这个例子RectangleExample
为这个类加入一个可选参数的构造函数,最后再加一个toSting。
class Rectangle {
int width;
int height;
Point origin;
Rectangle({Point origin = const Point(0, 0), int width, int height}) {
this.origin = origin;
this.width = width;
this.height = height;
}
@override
String toString() =>
'Origin: (${origin.x}, ${origin.y}), width: $width, height: $height';
}
这里坐标orgin设置了默认参数。注意,使用默认参数时,需要将参数包裹在{ }中。之后再main函数里进行调用:
void main() {
print(Rectangle(origin: const Point(10, 20), width: 100, height: 200));
print(Rectangle(origin: const Point(10, 10)));
print(Rectangle(width: 200));
print(Rectangle());
// 注意:这样写会报错,需要加参数名
print(Rectangle(100, 100));
}
注意,这里需要填写参数名称,不能直接给值。这一点上相比于Kotlin确实要麻烦一些。
最后控制台的输出是这样的:
Origin: (10, 20), width: 100, height: 200
Origin: (10, 10), width: null, height: null
Origin: (0, 0), width: 200, height: null
Origin: (0, 0), width: null, height: null
使用工厂模式
使用工厂模式创建类的实例是开发中常用的情况。Dart对此还特意提供了一个factory
关键字。下面我们来介绍一下如何在Dart中快速实现工厂模式。先看这个例子:
import 'dart:math';
abstract class Shape {
num get area;
}
class Circle implements Shape {
final num radius;
Circle(this.radius);
num get area => pi * pow(radius, 2);
}
class Square implements Shape {
final num side;
Square(this.side);
num get area => pow(side, 2);
}
这里有几个知识点:
- 第一行的import,说明Dart提供了数学运算等一系列基础库,例如
dart:core
,dart:async
,dart:convert
,dart:collection
,这个后面可以自己去看API详细了解 - 和Kotlin一样,一个文件可以定义多个类
- Dart也是支持抽象类的,继承的时候,使用
implements
关键字。Dart中没有interface接口的概念,这个后面再细讲。
下面针对父类Shape
加入一个工厂构造函数
,使用关键字factory
,并且在main函数中调用,代码如下:
abstract class Shape {
num get area;
factory Shape(String type) {
if (type == 'circle') return Circle(2);
if (type == 'square') return Square(2);
throw 'Can\'t create $type.';
}
}
void main() {
try {
print(Shape('circle').area);
print(Shape('square').area);
print(Shape('triangle').area);
} catch (err) {
print(err);
}
}
输出如下:
12.566370614359172
4
Can't create triangle.
观察这段代码,里面引入了异常处理机制。使用工厂函数创建的时候,如果传入的类型不合法,会throw
一个异常。然后在main函数中使用try/catch
函数来捕获这个异常并处理。另外,因为示例代码中用单引号包裹字符串,所以异常文案中的Can't creat
用到了转义字符,用双引号包裹字符串也是可以的。
当然除了上面介绍的使用factory
关键字创建工厂构造函数之外,你也可以像Kotlin一样,自己写一个工厂函数,例如:
Shape shapeFactory(String type) {
if (type == 'circle') return Circle(2);
if (type == 'square') return Square(2);
throw 'Can\'t create $type.';
}
这个效果是一样的,觉得那种顺手就用哪一种吧。
示例代码在这里
Interface接口问题
前面说了,Dart没有Java里面Interface的概念。为什么呢?我来告诉你为什么?你说为什么?!
额,抱歉,写了这长了,偶尔皮一下。
实际上是因为每一个Dart类都隐形的有一个接口。到底什么意思呢?来看看刚才的例子
在其中加入一个CircleImpl类继承Circle:
class CircleImpl implements Circle {}
运行发现会报错:
Error: The non-abstract class 'CircleImpl' is missing implementations for these members:
- 'radius'
- 'area'
这是因为Circle中含有radius和area两个属性,虽然Circle类中已经实现了这两个参数的处理,但是Circle类并没有继承到具体实现。
所以Dart的继承并不是真正意义上的继承那个类,而是继承这个类对应的数据结构的接口,这个数据结构包括它的参数和函数,但是不包括实现。这也是为什么Dart继承的关键字是implement
而不是extends
CircleImpl类继承的不是Circle,只是继承了它的数据结构对应的接口,只是告诉你需要有radius和area两个属性,但是没有把实现继承给你。
所以我们把CircleImpl
类改成这样就好了:
class CircleImpl implements Circle {
num radius;
num area;
}
可以自己尝试写一个带函数的类,然后写个子类继承它,看看是什么效果?
函数式编程
函数式编程对于熟练使用Kotlin和Swift的人应该都不陌生了,Dart也是有相应的特性的。
下面我们来介绍一下,首先是函数的返回可以直接是一个表达式,这个前面都见过:
// 函数式风格写法
String sayHello() => "Hello World";
// 等价于传统返回值写法
String sayHello() {
return "Hello World";
}
再来看看映射操作、集合遍历和将函数作为参数的特性:
String sayHello(int time) => "Hello World! $time";
main() {
final values = [1, 2, 3, 4, 5, 6];
values.map(sayHello).forEach(print);
}
// 输出结果
Hello World! 1
Hello World! 2
Hello World! 3
Hello World! 4
Hello World! 5
Hello World! 6
这里首先用到了map映射,values是一个int数组,用map映射,将int通过函数sayHello变成了一个字符串数组。之后再用集合遍历forEach,遍历的调用print函数打印结果。这里map和forEach都是将一个函数当做参数接收了。
此外Dart集合框架中支持map和set数据结构,而且提供了类似Kotlin的那些便捷函数,例如下面这个写法,就是跳过第一个元素,之后再取前三个元素进行处理:
main() {
final values = [1, 3, 5, 7, 9, 11];
values.skip(1).take(3).map(sayHello).forEach(print);
}
// 输出结果
Hello World! 2
Hello World! 3
Hello World! 4
最后说说控制流,为什么写到最后还不讲讲控制流呢,因为Dart的控制流和Java真的一模一样,什么if/else啊,switch啊都一样,除了没有when语句。这个随便写写就知道了。
写在最后
恭喜你已经入坑了。读到这里你已经掌握了Dart语言的全部基础知识!怎么样不难吧,虽然Dart冷门了点,但是技多不压身嘛。
详细的类库API可以查阅Dart语言官网
。这里写的非常详细。就像当年刚学Java的时候看JavaAPI一样使用它就行了。
后面我会再写写Flutter的游玩感受。这个东西我个人认为目前就是调研调研,简单了解一下就好,没必要深入学习。其实Dart语言看完这篇文章就够用了,太细节的类库API用到的时候查一下就好了。
网友评论