一掂林、前言
在使用Java
語(yǔ)言設(shè)計(jì)類(lèi)之間關(guān)系的時(shí)候志鞍,我們會(huì)接觸到 組成單元 和 關(guān)系連接 這兩類(lèi)概念:
- 組成單元:普通類(lèi)聂薪、
abstract
抽象類(lèi)家乘,interface
接口。 - 關(guān)系連接:
implements
實(shí)現(xiàn)藏澳,extends
繼承仁锯。
而在Dart
當(dāng)中,對(duì)于這兩類(lèi)概念進(jìn)行了增減:
- 組成單元:普通類(lèi)翔悠,
abstract
抽象類(lèi)业崖、mixin
野芒。 - 關(guān)系連接:
implements
實(shí)現(xiàn)、extends
繼承双炕、with
混入复罐。
最大的不同有兩點(diǎn):
- 去掉了
interface
。 - 增加了混入的概念雄家。
下面我們就來(lái)看一下其中涉及到的知識(shí)點(diǎn)效诅,前面兩節(jié)對(duì)比一下Java
和Dart
的區(qū)別,最后著重介紹混入的概念趟济。
推薦給大家一個(gè)網(wǎng)站:https://dartpad.dartlang.org/ 可以在線(xiàn)運(yùn)行乱投。
二、組成單元
2.1 普通類(lèi)
Java
和Dart
的普通類(lèi)有區(qū)別顷编,但是不影響我們?cè)O(shè)計(jì)類(lèi)之間的關(guān)系戚炫,因此不再贅述。
2.2 abstract 抽象類(lèi)
Java
和Dart
的抽象類(lèi)定義時(shí)大體是一樣的媳纬,我們可以在其中定義變量双肤、普通方法、抽象方法钮惠,它和普通類(lèi)最大的區(qū)別就是 抽象類(lèi)不能實(shí)例化茅糜。
Java
和Dart
在使用抽象類(lèi)時(shí)有一點(diǎn)不同:Dart
在定義抽象方法時(shí),不需要用abstract
修飾素挽。
abstract class DartAbs {
void absMethod();
}
2.3 interface 接口
在Dart
中蔑赘,沒(méi)有 interface 關(guān)鍵字。
順帶我們復(fù)習(xí)一下Java
中abstract
和interface
的一些要點(diǎn):
- 抽象類(lèi)和接口都不能被實(shí)例化预明。
- 抽象類(lèi)要被子類(lèi)繼承缩赛,接口要被類(lèi)實(shí)現(xiàn)。
- 接口只能做方法的聲明撰糠,抽象類(lèi)可以做方法的聲明酥馍,也可以做方法的實(shí)現(xiàn)。
- 接口里定義的變量只能是公共的靜態(tài)常量阅酪,抽象類(lèi)中的變量可以是普通變量旨袒。
- 抽象類(lèi)里的抽象方法必須全部被子類(lèi)實(shí)現(xiàn);接口的接口方法必須全部被子類(lèi)實(shí)現(xiàn)遮斥,否則只能為抽象類(lèi)峦失。
- 抽象類(lèi)里可以沒(méi)有抽象方法。
- 如果一個(gè)類(lèi)里有抽象方法术吗,那么這個(gè)類(lèi)只能是抽象類(lèi)尉辑。
- 抽象方法要被實(shí)現(xiàn),所以不能是靜態(tài)的较屿,也不能是私有的隧魄。
- 接口可繼承接口卓练,并可多繼承接口,但類(lèi)只能單繼承购啄。
三襟企、關(guān)系連接
3.1 extends
Java
和Dart
中extends
是一致的,只可以單繼承狮含,要注意的點(diǎn)是:
- 子類(lèi)可以繼承父類(lèi)里面 可見(jiàn)的屬性和方法顽悼。
- 對(duì)于
Java
來(lái)說(shuō),可見(jiàn)指的是非private
修飾几迄, - 對(duì)
Dart
來(lái)說(shuō)蔚龙,指的是非下劃線(xiàn)_
開(kāi)頭。
- 對(duì)于
- 子類(lèi)調(diào)用父類(lèi)的方法映胁,使用
super
關(guān)鍵字木羹。 - 子類(lèi)不會(huì) 繼承 父類(lèi)的構(gòu)造函數(shù)。
class Extends {
void base() {
print('base');
}
void log() {
print('extends');
}
}
class Log extends Extends {
log() {
print('log');
}
}
void main() {
Log().base();
Log().log();
}
輸出結(jié)果:
base
log
3.2 implements
implements
與extends
最大的不同就是允許后面接上多個(gè)普通或者抽象類(lèi)解孙,當(dāng)我們使用B implement A
修飾時(shí)坑填,那么A
中的所有的屬性和方法都要在A
中實(shí)現(xiàn),無(wú)論它原來(lái)是抽象方法還是普通方法弛姜。
也就是說(shuō)如果我們只想要A
中的接口定義脐瑰,而不想要它的實(shí)現(xiàn),那么就試用implements
娱据。
class Implements {
void base() {
print('base');
}
void log() {
print('extends');
}
}
class Log implements Implements {
base() {
print('log#base');
}
log() {
print('log');
}
}
void main() {
Log().base();
Log().log();
}
輸出結(jié)果:
log#base
log
四蚪黑、混入
前面我們介紹的都是Java
中接觸過(guò)的概念,下面我們來(lái)介紹Dart
中特有的概念 - 混入中剩。
4.1 mixin
mixin
用于修飾類(lèi),和abstract
類(lèi)似抒寂,該類(lèi)可以擁有成員變量结啼、普通方法、抽象方法屈芜,但是不可以實(shí)例化郊愧。mixin
一般用于描述一種具有某種功能的組塊,而某一對(duì)象可以擁有多個(gè)不同功能的組塊井佑。
(1) 最簡(jiǎn)單
最簡(jiǎn)單的mixin
由mixin & with
關(guān)鍵字組成属铁。
舉個(gè)例子,我們有一種能力是 '繪畫(huà)'躬翁,而擁有這種能力的是 ‘教師’焦蘑,那么實(shí)現(xiàn)如下:
mixin DrawFunc {
String content = '..';
String what();
void draw() {
print('I can draw ${what()}');
}
}
class Teacher with DrawFunc {
String what() => "car";
}
void main() {
Teacher().draw();
}
(2) 限定類(lèi)型
我們限定了 '繪畫(huà)' 這種能力只能夠用在 '人類(lèi)' 上面,示例如下:
class Person {}
mixin DrawFunc on Person {
String content = '..';
String what();
void draw() {
print('I can draw ${what()}');
}
}
class Teacher extends Person with DrawFunc {
String what() => "car";
}
void main() {
Teacher().draw();
}
當(dāng)我們?cè)?code>mixin上使用了on
關(guān)鍵字盒发,那么mixin
只能在那個(gè)類(lèi)的子類(lèi)上使用例嘱,而mixin
可以調(diào)用那個(gè)類(lèi)的方法狡逢。
(3) 多個(gè)類(lèi)型
在 '繪畫(huà)' 的基礎(chǔ)上,我們?cè)黾右环N新的能力 '唱歌'拼卵,示例如下:
class Person {}
mixin DrawFunc on Person {
String content = '..';
String what();
void draw() {
print('I can draw ${what()}');
}
}
mixin SingFunc on Person {
void sing() {
print('I can sing');
}
}
class Teacher extends Person with DrawFunc, SingFunc {
String what() => "car";
}
void main() {
Teacher().draw();
Teacher().sing();
}
(4) on 的一種復(fù)雜變形
關(guān)于on
還有一種復(fù)雜的變形奢浑,我們?cè)?'唱歌' 上增加一條約束,要求它必須是在DrawFunc
之上:
mixin SingFunc on Person, DrawFunc {
void sing() {
print('I can sing');
}
}
那么這時(shí)候腋腮,雖然Teacher
沒(méi)有extends DrawFunc
雀彼,但是如下的代碼仍然可以編譯通過(guò):
class Teacher extends Person with DrawFunc, SingFunc {
String what() => "car";
}
而我們交換一下DrawFunc
和SingFunc
的順序就不行了:
class Teacher extends Person with SingFunc, DrawFunc {
String what() => "car";
}
提示信息是:
Error compiling to JavaScript:
main.dart:22:7:
Error: 'Person' doesn't implement 'DrawFunc' so it can't be used with 'SingFunc'.
- 'Person' is from 'main.dart'.
- 'DrawFunc' is from 'main.dart'.
- 'SingFunc' is from 'main.dart'.
class Teacher extends Person with SingFunc, DrawFunc {
^
Error: Compilation failed.
結(jié)論:要滿(mǎn)足
on
的要求,除了使用extends
之外即寡,還可以在with
列表中详羡,在它之前進(jìn)行聲明。在Flutter
的WidgetsFlutterBinding
中嘿悬,就涉及到了這一點(diǎn)的運(yùn)用实柠。
abstract class BindingBase {}
mixin ServicesBinding on BindingBase {}
mixin SchedulerBinding on BindingBase, ServicesBinding {}
mixin RendererBinding on BindingBase, ServicesBinding {}
class WidgetsFlutterBinding extends BindingBase with ServicesBinding, SchedulerBinding, RendererBinding {}
(5) Tips
在這上面,我們接觸了幾個(gè)新的概念mixin, on, with
:
-
mixin
:定義了組塊善涨。 -
on
:限定了使用mixin
組塊的宿主必須要繼承于某個(gè)特定的類(lèi)窒盐;在mixin
中可以訪(fǎng)問(wèn)到該特定類(lèi)的成員和方法。 -
with
:負(fù)責(zé)組合組塊钢拧,而with
后面跟的類(lèi)并不一定需要是mixin
的蟹漓,abstract class
和普通類(lèi)都是可以的,這一點(diǎn)需要注意源内,例如下面這樣:
class Person {}
mixin DrawFunc on Person {
String content = '..';
String what();
void draw() {
print('I can draw ${what()}');
}
}
mixin SingFunc on Person {
void sing() {
print('I can sing');
}
}
abstract class DanceFunc {
void dance() {
print('I can dance');
}
}
class Teacher extends Person with DrawFunc, SingFunc, DanceFunc {
String what() => "car";
}
void main() {
Teacher().draw();
Teacher().sing();
Teacher().dance();
}
4.2 沖突
如果同時(shí)存在extends, with
葡粒,并且它們都定義了相同的方法名,那么結(jié)果如何呢膜钓?我們來(lái)看下面的例子:
class Extends {
void log() {
print('extends');
}
}
mixin Mixins {
void log() {
print('mixin');
}
}
mixin Mixins2 {
void log() {
print('mixin2');
}
}
class Log extends Extends with Mixins, Mixins2 {}
void main() {
Log().log();
}
輸出結(jié)果:
mixin2
結(jié)論
-
with
修飾的會(huì)覆蓋extends
中修飾的同名方法嗽交。 -
with
列表中后一個(gè)的會(huì)覆蓋之前的。
再來(lái)看一下加上了implements
的情況颂斜。
class Extends {
void log() {
print('extends');
}
}
mixin Mixins {
void log() {
print('mixin');
}
}
mixin Mixins2 {
void log() {
print('mixin2');
}
}
class Implements {
void log() {
print('implements');
}
}
class Log extends Extends with Mixins, Mixins2 implements Implements {}
void main() {
Log().log();
}
輸出結(jié)果為:
mixin2
這里我們發(fā)現(xiàn)了一個(gè)奇怪的現(xiàn)象:雖然我們加上了implements
夫壁,但是Dart
居然沒(méi)讓我們實(shí)現(xiàn)Implements.log()
方法!
這是因?yàn)樵谶@種情況下沃疮,它識(shí)別到我們從with
和extends
中獲得了log()
方法的能力盒让,因此調(diào)用的是Mixins2.log()
。
假如我們對(duì)Implements#log
方法進(jìn)行實(shí)現(xiàn):
class Log extends Extends with Mixins, Mixins2 implements Implements {
void log() {
print("implements log");
}
}
輸出的結(jié)果為:
implements log
五司蔬、小結(jié)
梳理下來(lái)邑茄,有幾點(diǎn)感想:
- 之前我們?cè)O(shè)計(jì)中用到了
interface
的部分,可以采用只帶有抽象方法的abstract class
替換俊啼,并繼續(xù)使用implements
關(guān)鍵字肺缕。 - 理解
mixin
的概念,我是將它理解為一個(gè)個(gè)的功能組塊:哪些宿主需要哪些功能,我就with
到上去搓谆。 -
on
關(guān)鍵字一方面是為了限制組塊的應(yīng)用場(chǎng)景炒辉,也可以為多個(gè)組塊提供公共的基礎(chǔ)功能。