学习一门新的语言,最好的方法就是从自己熟悉的语言中找不同。这篇文章就从基本语法、逻辑判断和控制、属性、函数、接口、抽象类、类、集合、IO、泛型、异常、多线程、反射角度看 Kotlin 与 Dart 的区别。
基本语法的区别
变量声明
在 Dart 中,变量需要显示声明类型,代码如下所示:
// 整型 age,表示年龄的数值
int age = 2;
// 浮点型 weight,表示体重的数值
double weight = 4.5;
如果想要主动推动类型,可以使用关键字 var,它和 kotlin 中的 var 的作用是一致的。在 Dart 中还有一个关键字 dynamic,它是一种特殊的类型,使用 dynamic 声明的变量可以在运行时存储任何类型的值,并且可以随时改变其存储的值的类型。代码示例如下:
void main() {
// 使用 var 声明变量,初始值为字符串,Dart 推断其类型为 String
var message = 'Hello, Dart!';
print('message 的类型: ${message.runtimeType}');
// 使用 dynamic 声明变量
dynamic value;
// 赋值为字符串
value = 'Hello';
print('value 的类型: ${value.runtimeType}');
// 赋值为整数
value = 123;
print('value 的类型: ${value.runtimeType}');
// 调用 value 的方法,编译时不会检查,运行时根据实际类型调用
if (value is int) {
print(value + 1);
}
}
函数声明
在 Dart 中,函数的声明和 Java 类似,代码示例如下:
double bmi(double height, double wight) {
// 具体算法
double result = wight / (height * height);
return result;
}
kotlin 中支持给函数的参数设置默认值,调用时也可以使用形参传入数值。在 Dart 中通过命名参数实现,命名参数需要加上 {},代码示例如下:
double bmi({
required double height, // required 关键字表示该入参必须传入;
double weight = 65, // 可以用 `=` 提供参数的默认值
}) {
// 具体算法
double result = weight / (height * height);
return result;
}
void main() {
// 调用时通过 : 设置参数值
double toly = bmi(weight: 70, height: 1.8);
}
在 Dart 中还有一种参数是位置参数,它需要加上 [],代码示例如下:
// 位置参数,必须要有默认值
double bmi([double height = 1.79, double weight = 65]) {
// 具体算法
double result = weight / (height * height);
return result;
}
void main() {
double toly = bmi(1.8,70);
}
逻辑判断和控制
if
Dart 的 if 和 Java 一样,无法获取 if 表达式的返回值。代码示例如下:
void main() {
double height = 1.18;
// 布尔值可以通过运算获得
bool free = height < 1.2;
if(free){
print("可免费入园");
}else{
print("请购买门票");
}
}
switch
Dart 的 switch 也和 Java 一样,代码示例如下:
void main() {
String mark = 'A';
switch (mark) {
case 'A':
print("优秀");
break;
case 'B':
print("良好");
break;
case 'C':
print("普通");
break;
case 'D':
print("较差");
break;
case 'E':
print("极差");
break;
default:
print("未知等级");
}
}
for
Dart 中 for 的语法也和 Java 中一样,代码示例如下:
void main() {
int sum = 0;
for (int i = 0; i < 5; i = i + 1) {
sum = sum + i;
print("第 $i 次执行,sum = $sum");
}
}
---->[输出结果]----
第 0 次执行,sum = 0
第 1 次执行,sum = 1
第 2 次执行,sum = 3
第 3 次执行,sum = 6
第 4 次执行,sum = 10
while
Dart 中的 while 语法也和 Java 中一样,代码示例如下:
void main() {
int i = 0;
while (i < 10) {
double bmiValue = bmi(1.75, 65); // 示例:身高1.75m,体重65kg
print("BMI: $bmiValue\n");
i++;
}
}
Dart 中也同样支持 break 和 continue,它们的作用和在 Java 中是一样的。
~/
Dart 中的加减乘除的运算符和 Java 一样,不同的是 Dart 增加了一个 ~/ 运算符,用来求商,代码示例如下:
void main() {
print(10 % 3);//1 余
print(10 ~/ 3);//3 商
}
逻辑运算符
Dart 的逻辑运算符 && 、||、! 也和 Java 一样,代码示例如下:
void main() {
// 公园是否开放
bool open = true;
// 是否免费
bool free = false;
// 公园是否免费进入
bool freeEnter = open && free;
}
默认情况下,== 可以判断是否指向同一个实例,但是大部分数据类型都会重写该方法,让其判断值是否相同。因此Dart 提供了 identical 方法来判断两个对象是否指向同一个实例。
位运算符
在 Dart 中,左移、右移的运算符 也和 Java 一样,代码示例如下:
属性的区别
访问控制的区别
在 Dart 中,没有 public、private 等访问控制符。在 Dart 中,默认情况下,所有的类、变量、方法、函数等都是公开的,即可以在整个项目的任何地方访问。如果你想要限制访问,则需要在在标识符(类、变量、方法等)前加下划线 _ 来表示该成员是私有的,私有成员只能在定义它们的库(文件)内部访问。代码示例如下:
// 定义一个包含私有成员的类
class PrivateExample {
// 私有变量
String _privateVariable = 'This is a private variable';
// 私有方法
void _privateMethod() {
print('This is a private method');
}
// 公开方法,用于在类内部访问私有成员
void accessPrivateMembers() {
print(_privateVariable);
_privateMethod();
}
}
常量的区别
在 Dart 中可以使用 final 和 const 定义常量。const与final的共同点是初始化后都无法更改。两者的区别是,const值在编译时会检查值,而final值在运行时才检查值。因此不能给const定义的常量赋值为不确定的值。代码示例如下:
const time='2020-05-03';
const time=DataTime.now();//这行代码在编译器中会报错
final time='2020-05-03';
final time=DataTime.now();//这行代码不会报错
类型的区别
Dart 和 Kotlin 一样,一切都是对象,没有 Java 中的基本数据类型。但是 Dart 的数据类型与 Kotlin 有所不同。
可空类型
Dart 和 Kotlin 类似,也支持可空类型。不同的是,在 Dart 中使用 ! 来强制把可空类型转换为非可空类型;同时使用 ?? 来为可空类型提供一个默认值。代码示例如下:
int? nullableInt = null;
// 使用安全调用操作符,当 nullableInt 为 null 时,不会调用方法,直接返回 null
int? result = nullableInt?.toDouble()?.toInt();
print('result 的值: $result');
nullableInt = nullableInt ?? 0;
print('nullableInt 的值: $nullableInt');
需要注意的是,Dart 的 runtimeType 方法获取的是实际运行时的类型。比如对于 int? a 变量,它只会返回 int 类型或者 Null 类型。
数字类型(Numbers )
在 Dart 语言中,数字有两大类型: 整型(整数) int 和浮点型(小数) double ,没有 float 类型。定义变量的语法是 类型名 变量名 = 值,代码示例如下:
void main(){
// 整型 age,表示年龄的数值
int age = 2;
// 浮点型 weight,表示体重的数值
double weight = 4.5;
}
虽然 int、double 看上去像是 java 中的基本数据类型,但实际上它们都是继承 num 类。
布尔类型(bool)
Dart 的布尔类型使用 bool 表示,代码示例如下:
void main() {
// 直接赋值
bool enable = true;
double height = 1.18;
// 布尔值可以通过运算获得
bool free = height < 1.2;
}
字符串类型
在 Dart 中有3种创建字符串的方式:
- 使用单引号、双引号创建字符串。
- 使用3个单引号或者双引号创建多行字符串。
- 使用r创建原始字符串。
代码示例如下:
void main() {
// 使用双引号创建字符串
String doubleQuotedString = "Hello, Dart!";
print(doubleQuotedString);
// 使用三个单引号创建多行字符串
String multiLineSingleQuoted = '''
这是一个多行字符串示例。
它可以包含多行文本。
这里是第三行。
''';
print(multiLineSingleQuoted);
// 普通字符串,需要对反斜杠进行转义
String normalString = '这是一个包含路径的字符串:C:\\Users\\Documents';
print(normalString);
// 原始字符串,反斜杠无需转义
String rawString = r'这是一个包含路径的原始字符串:C:\Users\Documents';
print(rawString);
}
同时 String 还可以通过 + 拼接,也可以使用 $变量名 在字符串内插入变量值,这个和 kotlin 的语法是一样的。代码示例如下:
void main() {
String str1 = "Hello" + "World";
String str2 = "$str1 Dart";
}
列表类型(List)
在 Dart 中,列表表示集合,也表示数组,代码示例如下:
void main() {
String str1 = "Hello" + "World";
String str2 = "$str1 Dart";
}
列表中的常用方法有:add()、length()、remove()、insert()、indexOf()、sublist()、forEach()、shuffle()等,代码示例如下:
var list=["apple","banana","cherry"];
print(list.length);//输出列表的长度
list.add("bayberry");//末尾添加"bayberry字符串
print(list);
list.remove("apple")//删除apple字符串
print(list);
list.insert(1, 'dates');//在1索引插入dates字符串
print(list);
print(list.indexOf("cherry"));//获取cherry字符串所在位置
print(list.sublist(2));//去除前两个元素后的新的列表
list.forEach(print);//遍历并输出列表
list.shuffle();//打乱列表顺序
print(list);
键值对类型(Map)
示例如下:
Map<int, String> numMap = {
0: 'zero',
1: 'one',
2: 'two',
};
print(numMap);
numMap.remove(1);
print(numMap);
---->[控制台输出]----
{0: zero, 1: one, 2: two}
{0: zero, 2: two}
Set 类型
示例如下:
Set<int> numSet = {1, 9, 9, 4};
print(numSet);
---->[控制台输出]----
{1, 9, 4}
Set 最重要的特征是可以进行集合间的运算,这点 List 列表是无法做到的。两个集合间通过 difference、union 、intersection 方法可以分别计算差集、并集、交集。计算的结果也是一个集合,代码示例如下:
Set<int> a = {1, 9, 4};
Set<int> b = {1, 9, 3};
print(a.difference(b));// 差集
print(a.union(b)); // 并集
print(a.intersection(b)); // 交集
---->[控制台输出]----
{4}
{1, 9, 4, 3}
{1, 9}
类型判断与转换
在 Dart中,runtimeType 可以获取对象类型;is 和 is! 可以判断类型;而 as 可以强制把类型转换。代码示例如下:
void main() {
var obj1 = 'Hello, Dart!';
var obj = 'Hello';
// 判断 obj1 是否为 String 类型
bool isString = obj1 is String;
print('obj1 is String: $isString');
// 判断 obj 是否不是 int 类型
bool isNotInt = obj is! int;
print('obj is not int: $isNotInt');
}
静态变量的区别
在 Dart 中,静态变量和静态方法和 Java 一样都被 static 修饰,代码示例如下:
class Cat{
// 静态变量
static String TAG = 'Cat';
static String eatFish(){
print('猫天生喜欢吃鱼!');
}
}
函数的区别
构造函数的区别
Dart 的构造函数和 Java 类似,代码示例如下:
class Human {
String name = '';
double weight = 0;
double height = 0;
Human(String name,double weight,double height){
this.name = name;
this.weight = weight;
this.height = height;
}
// 上面的方法可以简写成下面这样
Human(this.name, this.weight, this.height)
}
由于 Dart 不支持函数重载,因此推出了命名构造函数来解决这个问题。代码示例如下:
class Human {
String name = '';
double weight = 0;
double height = 0;
int age = 0;
Human(String name, double weight, double height) {
this.name = name;
this.weight = weight;
this.height = height;
}
Human.withAge(this.name, this.weight, this.height, this.age);
}
在构造函数中还可以加上 factory 关键字,这表示工厂构造函数。工厂构造函数最大的特点是可以手动返回一个对象。代码示例如下:
class Person {
String name;
static final Map<String, Person> cache = {};
Person(this.name);
factory Person.getSingle(String name) {
if (cache.containsKey(name)) {
return cache[name] as Person;
} else {
cache[name] = new Person(name);
return cache[name] as Person;
}
}
}
高阶函数
和 Kotlin 类型,Dart 也可以把函数作为变量来传递,代码示例如下:
// 定义一个高阶函数,接受一个函数作为参数
int calculate(int a, int b, int Function(int, int) operation) {
return operation(a, b);
}
// 定义加法函数
int add(int a, int b) {
return a + b;
}
// 定义减法函数
int subtract(int a, int b) {
return a - b;
}
void main() {
int num1 = 10;
int num2 = 5;
// 调用 calculate 函数并传递 add 函数
int sum = calculate(num1, num2, add);
print('加法结果: $sum');
// 调用 calculate 函数并传递 subtract 函数
int difference = calculate(num1, num2, subtract);
print('减法结果: $difference');
}
单行函数
在 Dart 中,使用 => 用来表示 {},常用于单行函数中,代码示例如下:
int squareWithArrow(int num) => num * num;
级联运算符
级联运算符 (.., ?..) 可以让你在同一个对象上连续调用多个对象的变量或方法,它类似于 Kotlin 中的 apply{} 和 ?.apply{}。代码示例如下:
class Person {
String name = '';
int age = 0;
void introduce() {
print('我叫 $name,今年 $age 岁。');
}
}
void main() {
Person person = Person()
..name = '张三'
..age = 25
..introduce();
}
接口和抽象类的区别
在 Dart 中,没有 interface 关键字来定义接口。但是每一个类(除了 int、String 这些类型类不行)外都可以通过 implements 来作为接口来实现。
在 Dart 中,抽象类也是用 abstract 来声明的,抽象类可以用 implements 作为接口来实现;或者使用 extends 来作为父类来继承。
代码示例如下:
// 定义一个抽象类作为接口
abstract class Shape {
// 没有实现的是抽象方法,不需要加 abstract 修饰
double area();
void draw() {
print('绘制一个形状');
}
}
// implements 表示子类是作为接口来实现的
// 因此不能使用 Shape 中的任何属性和方法,同时必须重写所有的属性和方法
class Circle implements Shape {
double radius;
Circle(this.radius);
@override
double area() {
return 3.14 * radius * radius;
}
// 必须实现
@override
void draw() {
print('绘制一个半径为 $radius 的圆');
}
}
// extends 表示子类是作为父类来实现的
// 因此只需要实现抽象方法,同时可以使用Shape 中的属性和方法
class Rectangle extends Shape {
double width;
double height;
Rectangle(this.width, this.height);
@override
double area() {
return width * height;
}
}
类的区别
new 的区别
在 Kotlin 中,创建对象不需要 new 关键字,而在 Dart 中,new 是可选的。
继承的区别
在 Dart 中,一般的继承和 Java 一样。它们都是使用 extends 继承父类,使用 super 调用父类的方法。代码示例如下:
// 定义父类
class Animal {
String name;
// 父类构造函数
Animal(this.name);
// 父类方法
void eat() {
print('$name 正在进食');
}
}
// 定义子类,继承自 Animal 类
class Dog extends Animal {
// 子类构造函数,调用父类构造函数
Dog(String name) : super(name);
// 子类特有的方法
void bark() {
print('$name 正在汪汪叫');
}
}
void main() {
// 创建 Dog 类的实例
Dog dog = Dog('旺财');
// 调用从父类继承的方法
dog.eat();
// 调用子类特有的方法
dog.bark();
}
在 Kotlin 中,我们使用继承是为了复用父类的代码。而在 Dart 中,除了继承这种方式外,还提供了 mixin(混合)来让你在不使用继承的情况下,将一组方法和属性添加到多个类中。mixin 主要用于实现代码的复用和组合,避免了多重继承带来的复杂性和问题。代码示例如下:
// 定义一个 mixin
mixin CanFly {
void fly() {
print('正在飞行');
}
}
// 定义另一个 mixin
mixin CanSwim {
void swim() {
print('正在游泳');
}
}
// 定义一个类,使用 with 关键字应用 mixin
class Bird with CanFly {
void chirp() {
print('鸟儿在叽叽喳喳叫');
}
}
// 定义另一个类,应用多个 mixin
class Duck with CanFly, CanSwim {
void quack() {
print('鸭子在嘎嘎叫');
}
}
void main() {
Bird bird = Bird();
bird.fly();
bird.chirp();
Duck duck = Duck();
duck.fly();
duck.swim();
duck.quack();
}
需要注意,被 mixins 的类有如下限制:
- 只能继承自 Object,不能继承其他类
- 不能有构造函数
内部类的区别
和 Kotlin 不同,Dart 不支持内部类。
单例
在 Dart 中没有 Kotlin 中的 object 关键字来直接声明单例。Dart 中的单例声明如下:
class Singleton {
// 静态私有变量,用于存储单例实例
static final Singleton _instance = Singleton._internal();
// 私有构造函数,防止外部直接创建实例
Singleton._internal();
// 静态方法,用于获取单例实例
static Singleton get instance => _instance;
void doSomething() {
print('单例对象正在执行操作...');
}
}
void main() {
Singleton singleton1 = Singleton.instance;
Singleton singleton2 = Singleton.instance;
// 验证两个实例是否相同
print('singleton1 和 singleton2 是否为同一个实例: ${identical(singleton1, singleton2)}');
singleton1.doSomething();
}
枚举的区别
Dart 中的枚举的定义和 Kotlin 类似,代码示例如下:
// 定义一个枚举类型表示一周的天数
enum Weekday {
monday(name: "星期一"),
tuesday(name: "星期二"),
wednesday(name: "星期三"),
thursday(name: "星期四"),
friday(name: "星期五"),
saturday(name: "星期六"),
sunday(name: "星期天");
const Weekday({required this.name});
final String name;
}
更详细的用法可以看 Dart 的枚举类型的高阶用法Dart 的枚举你知道多少?
导入库的区别
不同于 Kotlin 通过import导入各种类型的开发包,Dart 将这些导入的开发包称为库,每段Dart程序都是由被称为库的模块化单元组成的。在 Dart 中也是通过 import 关键字来导入的,代码示例如下:
import 'dart:math'; // 引入内置 math 库,对于 Dart 内置的库,使用 `dart:xxxxxx` 的形式
import 'package:test/test.dart'; // 引入包管理器中的库
import 'lib/test.dart'; // 引入自己写的库
IO的区别
在 Dart 中的 IO 操作是使用的 dart:io 库,代码示例如下:
import 'dart:io';
void main() async {
try {
// 创建一个 File 对象,指定要读取的文件路径
File file = File('example.txt');
// 以 UTF-8 编码读取文件的全部内容
String content = await file.readAsString();
print('文件内容:$content');
} catch (e) {
print('读取文件时出错:$e');
}
}
更多IO操作可以看 Flutter系列之Dart文件IO操作_dart.io delete file-CSDN博客
泛型的区别
Dart 中泛型也是使用 <T> 来表示,也可以通过 extends 来限制。但是不同于 Kotlin,没有星投影、
逆变等操作。代码示例如下:
// 定义一个抽象类
abstract class Shape {
void draw();
}
// 定义一个矩形类,继承自 Shape
class Rectangle extends Shape {
@override
void draw() {
print('绘制矩形');
}
}
// 定义一个圆形类,继承自 Shape
class Circle extends Shape {
@override
void draw() {
print('绘制圆形');
}
}
// 定义一个泛型类,限制类型参数必须是 Shape 或其子类
class ShapeContainer<T extends Shape> {
T shape;
ShapeContainer(this.shape);
void drawShape() {
shape.draw();
}
}
void main() {
Rectangle rectangle = Rectangle();
ShapeContainer<Rectangle> rectangleContainer = ShapeContainer(rectangle);
rectangleContainer.drawShape();
Circle circle = Circle();
ShapeContainer<Circle> circleContainer = ShapeContainer(circle);
circleContainer.drawShape();
// 下面这行代码会报错,因为 int 不是 Shape 或其子类
// ShapeContainer<int> intContainer = ShapeContainer(123);
}
多线程的区别
Dart是一门单线程的语言,不支持多线程。具体可以看 Flutter-Dart中的异步和多线程讲解
异常的区别
和 Java 异常处理类似,Dart 使用 throw 抛出异常、try-catch-finally 来处理异常。不同的是,在 Dart 中,我们需要使用 on 关键字来指定异常的类型;也可以使用 rethrow 关键字在我们捕获异常时,再把这个异常抛出。代码示例如下:
try {
// throw Error();
throw Exception('this is exception error');
} on Exception catch (e) {
print('this is Unknown exception $e');
} catch (e,s) { // catch 方法有两个参数,第一个参数是抛出的异常对象,第二个参数是栈信息
print('No specified type, handles all error $e');
print('Stack trace:\n $s');
}
注解的区别
在 Dart 中,注解的样式和 Kotlin 一样都是 @注解名。不同的是,Dart 中自定义注解需要创建一个类。类名通常以Annotation为后缀。自定义注解类可以包含任意数量的参数,这些参数可以在使用注解时传入。要定义自定义注解,需要创建一个带有const构造函数的类。这个类可以有任意数量的属性,但它们必须是编译时常量。代码示例如下:
class MyAnnotation {
final String description;
const MyAnnotation(this.description);
}
// 使用自定义注解注解类
@MyAnnotation('这是一个注解类的元数据')
class MyClass {
// ...
}
// 使用自定义注解注解函数
@MyAnnotation('这是一个注解函数的元数据')
void myFunction() {
// ...
}
// 使用自定义元素据注解变量
@MyAnnotation('这是一个注解变量的元数据')
int myVariable;
在这个示例中,我们定义了一个名为MyAnnotation的自定义注解。这个注解有一个属性description,用于存储与注解关联的文本描述。我们还为这个类提供了一个const构造函数,以便在使用注解时可以创建编译时常量。
反射的区别
Dart 的反射具体可以看 Dart基础4-反射 - 简书