【Dart】dart之mixin探究

由于dart中是没有interface的,在dart中我们需要定义接口的话用的是class关键字

implements是把某个class当做接口来实现要求我们重写这个class的所有方法

注意:implements会将class的实现抹掉就不存在默认实现一说,而dart也是不允许多继承的。

extends表示继承某个class,可以继承父类实现了的方法

mixin实际上也是面向对象编程中的概念,用户与Mixin不是“is-a”的关系,而是“-able”关系

Mixin是面向对象程序设计语言中的类,提供了方法的实现。其他类可以访问mixin类的方法而不必成为其子类。[1]Mixin有时被称作"included"而不是"inherited"。mixin为使用它的class提供额外的功能,但自身却不单独使用(不能单独生成实例对象,属于抽象类)。因为有以上限制,Mixin类通常作为功能模块使用,在需要该功能时“混入”,而且不会使类的关系变得复杂。

dart语言里面我们可以使用with关键字实现mixin,将一个或者多个class混入另一个类:

class Base1 {
  void foo1() {
    print("foo1");
  }
}

class Base2 {
  void foo2() {
    print("foo2");
  }
}

class Child2 with Base1, Base2 {

}

没错,通过with多个类,可以实现类似多继承的效果。

既然允许with多个类,那么如果这些类中有个相同方法,那会出现什么事情?

实际上kotlin、java8使用接口的默认实现也会出现一样的问题,他们的处理方法是当出现相同方法的时候实现类需要手动指定使用哪个接口的默认实现,要不然编译会报错:

// java8
interface IBase1 {
    default void foo() {
        System.out.println("1");
    }
}

interface IBase2 {
    default void foo() {
        System.out.println("2");
    }
}

class Child implements IBase1, IBase2 {

    @Override
    public void foo() {
        IBase2.super.foo();
    }
}
//kotlin
interface IBase1 {
    fun foo() {
        println("1")
    }
}

interface IBase2 {
    fun foo() {
        println("2")
    }
}

class Child : IBase1, IBase2 {
    override fun foo() {
        super<IBase2>.foo()
    }
}

必须提到的是:无论是 extends、implements 还是 mixin,优先级最高的是在具体类中的方法。

线性化

而在dart中with里面越后面的类优先级越高:

class Base1 {
  void foo() {
    print("1");
  }
}

class Base2 {
  void foo() {
    print("2");
  }
}

class Base3 {
  void foo() {
    print("3");
  }
}

class Child extends Base1 with Base2, Base3 {}

这个时候调用Child.foo方法实际会优先调用Base3.foo。原因是dart实际是通过创建中间类继承实现的mixin,上面的代码相当于:

img

通过从左到右的顺序生成中间父类去继承将extends、with线性化成一个单继承链。所以Base2、Base3实际上不是Child的父类

mixin关键字

在上面的例子中我们使用普通的class去with,但dart实际上提供了一个mixin关键字,它定义了不能实例化、也不能extends只能with的类:

mixin Base {

}

// 编译失败: mixin类不能extends
// class Child extends Base {
//
// }

// 编译成功: mixin类可以with
class Child with Base {

}

void main() {
  // 编译失败: mixin类不能实例化
  // Base()
}

这样的类实际上和java、kotlin里面的interface已经很像了。

另外我们可以通过mixin … on 限定某个类只能由某些类去with:

class Base1 {
  void foo() {
    print("1");
  }
}

class Base2 {
  void foo() {
    print("2");
  }
}

mixin Base3 on Base1 {
  void foo() {
    super.foo();
    print("3");
  }
}

class Child extends Base1 with Base2, Base3 {}

上面的demo中Base3只能由Base1去with,那就以为着这个with Base3的类一定是继承或者with了 Base1,所以可以调用这个类的super.foo方法。要注意的是,这个super.foo并不指定一定调用的是Base1.foo。例如上面的代码调用Child().foo()之后的打印实际上是:

2
3

它们线性化的到的继承关系和前面全是class的代码并没有差别:

img

从上面的uml图我们就能理解为什么打印是2 3了

理解了这个简单的例子之后我们再来看一个复杂一点的例子:

class Base1 {
  void foo1() {
    print("Base1.foo1");
    foo2();
  }

  void foo2() {
    print("Base1.foo2");
  }
}

mixin Base2 on Base1 {
  void foo1() {
    super.foo1();
    print("Base2.foo1");
  }

  void foo2() {
    print("Base2.foo2");
  }
}

class Child with Base1,Base2 {}

void main() {
  Child().foo1();
}

它的输出是:

Base1.foo1
Base2.foo2
Base2.foo1

原因是Base2.foo1中的super.foo1实际上调用的是Base1.foo1,而Base1.foo1中的foo2,由于继承的多态特性,调用的是Base2.foo2。

我们可以通过下面uml图辅助理解,注意看继承关系里面是没有Base1、Base2的因为它们都是通过with混入的,并不是Child的父类:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fLZsiSq3-1659324422994)(https:upload-images.jianshu.io/upload_images/2199790-7482262610cc24c7.png?imageMogr2/auto-orient/strip|imageView2/2/w/758)]

with的类不能有构造函数

另外,with的class和mixin类型都是不允许有构造函数的,因为mixin机制语义上是向一个类混入其他类的方法或者成员变量,使得我们可以在混合类中访问到混入类的方法或者属性。而混入其他类的构造函数实际上是没有意义的,因为不会有人手动去调用这个混入类的构造函数。

class Base1 {
  Base1() {}
}

// 编译失败: 不能with一个带有构造函数的类
// class Child with Base1 {}

// 编译失败: mixin类型只能with,所以不能有构造函数
// mixin Base2 {
//   Base2() {}
// }

参考博客

https://medium.com/flutter-community/dart-what-are-mixins-3a72344011f3
https://www.jianshu.com/p/f4efaa6b8fe6
https://www.jianshu.com/p/fc96bef9beba
https://my.oschina.net/zzxzzg/blog/2962518

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值