Flutter 语法进阶抽象类和接口本质区别详解

目录
  • 1. 接口存在的意义?
  • 2. 继承 VS 实现
  • 3. Dart 中接口与实现的特殊性
  • 4.Dart 中抽象类作为接口的小细节

1. 接口存在的意义?

在 Dart 中 接口 定义并没有对应的关键字。可能有些人觉得 Dart 中弱化了 接口 的概念,其实不然。我们一般对接口的理解是:接口是更高级别的抽象,接口中的方法都是 抽象方法 ,没有方法体。通过接口的定义,我们可以通过定义接口来声明功能,通过实现接口来确保某类拥有这些功能。

不过你有没有仔细想过,为什么接口会存在,引入接口的概念是为了解决什么问题?可能有人会说,通过接口,可以规范一类事物的功能,可以面向接口进行操作,从而可以更加灵活地进行拓展。其实这只是接口的作用,而且这些功能 抽象类 也可以支持。所以接口一定存在什么特殊的功能,是抽象类无法做到的。

都是抽象方法的抽象类,和接口有什么本质的区别呢?在我的初入编程时,这个问题就伴随着我,但渐渐地,这个问题好像对编程没有什么影响,也就被遗忘了。网上很多文章介绍 抽象类 和 接口 的区别,只是在说些无关痛痒的形式区别,并不能让我觉得接口存在有什么必要性。

思考一件事物存在的本质意义,可以从没有这个事物会产生什么后果来分析。现在想一下,如果没有接口,一切的抽象行为仅靠 抽象类 完成会有什么局限性 或说 弊端。没有接口,就没有 实现 (implements) 的概念,其实这就等价于在问 implements 消失了,对编程有什么影响。没有实现,类之间就只能通过 继承 (extends) 来维护 is-a 的关系。所以就等价于在问 extends 有什么局限性 或说 弊端。答案呼之欲出:多继承的二义性 。

那问题来了,为什么类不能支持 多继承 ,而接口可以支持 多实现 ,继承 和 实现 有什么本质的区别呢?为什么 实现 不会带来 二义性 的问题,这是理解接口存在关键。

2. 继承 VS 实现

下面我们来探讨一下 继承 和 实现 的本质区别。如下 A 和 B 类,有一个相同的成员变量和成员方法:

class A{
  String name;
  A(this.name);
  void run(){  print("B"); }
}
class B{
  String name;
  B(this.name);
  void run(){ print("B"); }
}

对于继承而言 派生类 会拥有基类的成员变量与成员方法,如果支持多继承,就会出现两个问题:

  • 问题一 : 基类中有同名 成员变量 ,无法确定成员的归属类
  • 问题二: 基类中有同名 成员方法 ,且子类未覆写。在调用时,无法确定执行哪个。
class C extends A , B {
  C(String name) : super(name); // 如果多继承,该为哪个基类的 name 成员赋值 ??
}
void main(){
  C c = C("hello")
  c.run(); // 如果多继承,该执行哪个基类的 run 方法 ??
}

其实仔细思考一下,一般意义上的接口之所以能够 多实现 ,就是通过限制,对这两个问题进行解决。比如 Java 中:

  • 不允许在接口中定义普通的 成员变量 ,解决问题一。
  • 在接口中只定义抽象成员方法,不进行实现。而是强制派生类进行实现,解决问题二。
abstract class A{
  void run();
}
abstract class B{
  void run();
}
class C implements A,B{
  @override
  void run() {
    print("C");
  }
}

到这里,我们就认识到了为什么接口不存在 多实现 的二义性问题。这就是 继承 和 实现 最本质的区别,也是 抽象类 和 接口 最重要的差异。从这里可以看出,接口就是为了解决多继承二义性的问题,而引入的概念,这就是它存在的意义。

3. Dart 中接口与实现的特殊性

Dart 中并不像 Java 那样,有明确的关键字作为 接口类 的标识。因为 Dart 中的接口概念不再是 传统意义 上的狭义接口。而是 Dart 中的任何类都可以作为接口,包括普通的类,这也是为什么 Dart 不提供关键字来表示接口的原因。

既然普通类可以作为接口,那多实现中的 二义性问题 是必须要解决的,Dart 中是如何处理的呢? 如下是 A 、B 两个普通类,其中有两个同名 run 方法:

class A{
  void run(){
    print("run in a");
  }
}
class B{
  void run(){
    print("run in a");
  }
  void log(){
    print("log in a");
  }
}

当 C 类实现 A 、B 接口,必须强制覆写 所有 成员方法 ,这点解决了二义性的 问题二 :

那 问题一 中的 成员变量 的歧义如何解决呢?如下,在 A 、B 中添加同名的成员变量:

class A{
  final String name;
  A(this.name);
  // 略同...
}
class B{
  final String name;
  B(this.name);
  // 略同...
}

当 C 类实现 A 、B 接口,必须强制覆为 所有 成员变量提供 get 方法 ,这点解决了二义性的 问题一 :

这样,C 就可以实现两个普通类,而避免了二义性问题:

class C implements A, B {
  @override
  String get name => "C";
  @override
  void log() {}
  @override
  void run() {}
}

其实,这是 Dart 对 implements 关键字的功能加强,迫使派生类必须提供 所有 成员变量的 get 方法,必须覆写 所有 成员方法。这样就可以让 类 和 接口 成为两个独立的概念,一个 class 既可以是类,也可以是接口,具有双重身份。

其区别在于,在 extend 关键字后,表示继承,是作为类来对待;

在 implements 关键字之后,表示实现,是作为接口来对待。

4.Dart 中抽象类作为接口的小细节

我们知道,抽象类中允许定义 普通成员变量/方法 。下面举个小例子说明一下 继承 extend 和 实现 implements 的区别。对于继承来说,派生类只需要实现抽象方法即可,抽象基类 中的普通成员方法可以不覆写:

而前面说过,implements 关键字要求派生类必须覆写 接口 中的 所有 方法 。也就表示下面的 C implements A 时,也必须覆写 log 方法。从这个例子中,可以很清楚地看出 继承 和 实现 的差异性。

抽象类 和 接口 的区别,就是 继承 和 实现 的区别,在代码上的体现是 extend 和 implements 关键字功能的区别。只有理解 继承 的局限性,才能认清 接口 存在的必要性。

以上就是Flutter 语法进阶抽象类和接口本质区别详解的详细内容,更多关于Flutter 语法抽象类接口的资料请关注我们其它相关文章!

(0)

相关推荐

  • Flutter的键值存储数据库使用示例详解

    目录 Flutter 键值存储数据库 unqlite unqlite_flutter 快速上手 简单键值对存储 JSON 为什么你应该使用unqlite_flutter? Flutter 键值存储数据库 键值存储是开发中十分常见的需求,在Flutter开发中,一般使用 shared_preferences 插件来实现.shared_preferences 本质上就是将键值对保存到一个XML文件中进行持久化. 而shared_preferences 实际上存在一定缺陷,譬如其性能较差,不适合处理大

  • Flutter之可滚动组件子项缓存 KeepAlive详解

    本文为大家分析了Flutter之可滚动组件子项缓存 KeepAlive,供大家参考,具体内容如下 首先回想一下,在介绍 ListView 时,有一个addAutomaticKeepAlives 属性我们并没有介绍,如果addAutomaticKeepAlives 为 true,则 ListView 会为每一个列表项添加一个 AutomaticKeepAlive 父组件.虽然 PageView 的默认构造函数和 PageView.builder 构造函数中没有该参数,但它们最终都会生成一个 Sli

  • 详解Flutter中数据传递的方式

    目录 1.构造方法传递 2.InheritedWidget 3.Notification 4.Stream & event_bus 在Flutter中,常见的数据传递一共有以下几种: 1.构造方法传递 Flutter的构造方法具备着dart语言的特点,参数具备可选状态,通过构造方法传递数据,可以很方便的将任意数据进行传递,平时开发中,A跳转B页面最常用的方法就是通过构造方法进行传递.比如我们最常见的Key就是通过构造一级一级向下传递的. 优点: 相邻页面之间传递数据非常方便,你不需要进行任何额外

  • Flutter自定义底部导航栏的方法

    本文实例为大家分享了Flutter自定义底部导航栏的具体代码,供大家参考,具体内容如下 文件结构: main.dart import 'package:flutter/material.dart'; import 'pages/tabs.dart';   void main() => runApp(new MyApp());   class MyApp extends StatelessWidget {   @override   Widget build(BuildContext contex

  • Flutter定义tabbar底部导航路由跳转的方法

    本文实例为大家分享了Flutter定义tabbar底部导航路由跳转的具体代码,供大家参考,具体内容如下 效果展示 整体实现的目录结构 第一步 把三个页面放到tabs里 Category.dart || Home.dart || Setting.dart 在这里我只展示 Home.dart 另外两个页面相同 import 'package:flutter/material.dart'; class HomePage extends StatefulWidget {   HomePage({Key

  • Flutter实现不同缩放动画效果详解

    目录 需求背景 可缩放组件介绍 ScaleTransition SizeTransition AnimatedSize AnimatedBuilder 小结 需求背景 组件缩放可以向着一个方向进行缩放,放大列表中某一个Cell期望它是向后进行放大而非组件中心点开始缩放.具体效果如下图所示: 可缩放组件介绍 ScaleTransition ScaleTransition具体实现如下代码,设置AnimationController控制器若需要增加数值操作可以再增加Animate再调用forward方

  • Flutter 语法进阶抽象类和接口本质区别详解

    目录 1. 接口存在的意义? 2. 继承 VS 实现 3. Dart 中接口与实现的特殊性 4.Dart 中抽象类作为接口的小细节 1. 接口存在的意义? 在 Dart 中 接口 定义并没有对应的关键字.可能有些人觉得 Dart 中弱化了 接口 的概念,其实不然.我们一般对接口的理解是:接口是更高级别的抽象,接口中的方法都是 抽象方法 ,没有方法体.通过接口的定义,我们可以通过定义接口来声明功能,通过实现接口来确保某类拥有这些功能. 不过你有没有仔细想过,为什么接口会存在,引入接口的概念是为了解

  • C#中抽象类与接口的区别详解

    1.面向接口编程和面向对象编程是什么关系 首先,面向接口编程和面向对象编程并不是平级的,它并不是比面向对象编程更先进的一种独立的编程思想,而是附属于面向对象思想体系,属于其一部分.或者说,它是面向对象编程体系中的思想精髓之一. 2.接口的本质 接口,在表面上是由几个没有主体代码的方法定义组成的集合体,有唯一的名称,可以被类或其他接口所实现(或者也可以说继承).它在形式上可能是如下的样子: interface InterfaceName { void Method1(); void Method2

  • PHP抽象类与接口的区别详解

    对于面向对象开发,抽象类与接口这两个东西是比较难理解的:就算是对于有一定经验的程序员来说也如此.下面根据自己的理解来讲述一下这两个东西,如有什么不对的,还望不吝赐教. 抽象类:是基于类来说,其本身就是类,只是一种特殊的类,不能直接实例,可以在类里定义方法,属性.类似于模版,规范后让子类实现详细功能. 接口:主要基于方法的规范,有点像抽象类里的抽象方法,只是其相对于抽象方法来说,更加独立.可让某个类通过组合多个方法来形成新的类. 抽象类与接口的相同点: 1.都是用于声明某一种事物,规范名称.参数,

  • Java详细讲解不同版本的接口语法和抽象类与接口的区别

    目录 什么是接口? 接口的语法: (JDK7.0) 接口的语法: (JDK8.0) 接口的语法: (JDK9.0)—(私有方法) 接口的分类 常量接口: 空接口: 函数式接口: 什么是接口? 说到接口,USB大家肯定不陌生~接口是一种标准.规范.注意:接口一旦制定好,使用者和实现者都必须遵循的标准. 接口的语法: (JDK7.0) (1) 关键字:interface (2) 语法:  interface 接口名{} (3) 接口编译之后会生成对应的 .class文件 (4) 接口不能创建对象,但

  • Vue页面切换和a链接的本质区别详解

    Vue-router是伴随着Vue框架出现的路由系统,它也是公认的一种优秀的路由解决方案.在使用Vue-router时候,我们常常会使用其自带的路径跳转组件Link,通过<Link to="path"></Link>实现跳转,这和传统的<a href="path" rel="external nofollow" ></a>何其相似!但它们到底有什么具体的区别呢? 对比<a>,Link组件

  • PHP抽象类和接口用法实例详解

    本文实例讲述了PHP抽象类和接口用法.分享给大家供大家参考,具体如下: 前言 对于oop,估计大多数人并不陌生.有些人除PHP外也学习不少其他语言,会发现php的不同之处,可能语法极其丑陋,但并不妨碍它成为世界上最好的语言(邪教语言).PHP可以允许常量作为接口的一部分,而对于抽象的理解十分重要. 计算机上,对抽象的理解与自然语言中我们每天使用的抽象概念有所不同.比如,我们指代'狗'.'猫'等动物,我们会说'那只狗/猫',他们就是具有狗/猫这类特征的具体实例.但是我们不能把猫和狗看作一类,也就是

  • Spring中BeanFactory与FactoryBean接口的区别详解

    前言 Spring框架中的BeanFactory接口和FactoryBean接口因为名称相似,老是容易搞混淆,而且也是面试过程中经常会碰到的一个问题.所以本文就专门给大家整理出来. 一.BeanFactory接口 BeanFactory接口是Spring容器的核心接口,负责:实例化.定位.配置应用程序中的对象及建立这些对象间的依赖. Spring为我们提供了许多易用的BeanFactory实现,XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系.

  • java 抽象类与接口的区别总结

    java 抽象类与接口的区别总结 abstract class和interface是Java语言中对于抽象类定义进行支持的两种机制,正是由于这两种机制的存在,才赋予了Java强大的面向对象能力. abstract class和interface之间在对于抽象类定义的支持方面具有很大的相似性,甚至可以相互替换,因此很多开发者在进行抽象类定义时对于 abstract class和interface 选择显得比较随意. 其实,两者之间还是有很大的区别的,对于它们的选择甚至反映出对于问题领域本质的 理解

  • Java中接口和抽象类的区别详解

    需求:接口是否可继承接口?抽象类是否可实现(implements)接口?抽象类是否可继承实体类(concrete class)?抽象类中是否可以有静态的main方法? 先说明二者的定义,然后聊聊需求,最后分析二者的区别. 含有abstract修饰符的类即为抽象类,抽象类不能创建实例对象.含有抽象方法的类必须定义为abstract class.在abstract class中,方法不必是抽象的,但是抽象方法必须在具体子类中实现,所以,不能有抽象构造方法或抽象静态方法.子类如果没有实现抽象父类中的所

  • Java 接口和抽象类的区别详解

    什么是抽象类和接口? 区别在哪里? 不同的编程语言对接口和抽象类的定义方式可能有些差别,但是差别并不大.本文使用 Java 语言. 抽象类 下面我们通过一个例子来看一个典型的抽象类的使用场景. Logger 是一个记录日志的抽象类,FileLogger 和 MessageQueueLogger 继承Logger,分别实现两种不同的日志记录方式: 记录日志到文件中 记录日志到消息队列中 FileLogger 和 MessageQueuLogger 两个子类复用了父类 Logger 中的name.e

随机推荐