Fultter学习笔记04

学习笔记04

东西还是比较多的,学习一门语言还是比较花时间的,有了C/C++的基础确实会好一点,但是怎么说一个星期的时间还是要画下去的,不然怕是嚼不烂。


主要是根据博客来学习的。


个人感觉是最重要的一个部分,也是必须要会用的一部分。

构造函数

1.对象

  • Dart 是一种面向对象的语言,并且支持基于mixin的继承方式

  • Dart 语言中所有的对象都是某一个类的实例,所有的类有同一个基类–Object

  • 基于mixin的继承方式具体是指:一个类可以继承自多个父类。

  • 但是Dart是单继承,同时可以继承多个“接口”。

  • 使用new语句来构造一个类,构造函数的名字可能是ClassName,也可以是ClassName.identifier, 例如:

      var jsonData = JSON.decode('{"x":1, "y":2}');
      
      // Create a Point using Point().
      var p1 = new Point(2, 2);
      
      // Create a Point using Point.fromJson().
      var p2 = new Point.fromJson(jsonData);  
    
  • 使用.(dot)来调用实例的变量或者方法。

      var p = new Point(2, 2);
      
      // Set the value of the instance variable y.
      p.y = 3;
      
      // Get the value of y.
      assert(p.y == 3);
      
      // Invoke distanceTo() on p.
      num distance = p.distanceTo(new Point(4, 4));  
    
  • 使用?.来确认前操作数不为空, 常用来替代. , 避免左边操作数为null引发异常。

  • 使用const替代new来创建编译时的常量构造函数。

      var p = const ImmutablePoint(2, 2);
    
  • 使用runtimeType方法,在运行中获取对象的类型。该方法将返回Type 类型的变量。

      print('The type of a is ${a.runtimeType}');  
    

2.实例化变量

  • 在类定义中,所有没有初始化的变量都会被初始化为null

      class Point {
      num x; // Declare instance variable x, initially null.
      num y; // Declare y, initially null.
      num z = 0; // Declare z, initially 0.
      }  
    
  • 类定义中所有的变量, Dart语言都会隐式的定义setter方法,针对非空的变量会额外增加getter方法。

      class Point {
      num x;
      num y;
      }
      	
      main() {
      var point = new Point();
      point.x = 4;          // Use the setter method for x.
      assert(point.x == 4); // Use the getter method for x.
      assert(point.y == null); // Values default to null.
      }  
    

3.构造函数

  • Dart 语言中,子类不会继承父类的命名构造函数。如果不显式提供子类的构造函数,系统就提供默认的构造函数。

  • 使用命名构造函数从另一类或现有的数据中快速实现构造函数。

      class Point {
      	num x;
      	num y;
      
      	Point(this.x, this.y);
      
      	// 命名构造函数Named constructor
      	Point.fromJson(Map json) {
      	 x = json['x'];
      	 y = json['y'];
      	}
      }  
    
  • 构造函数不能被继承,父类中的命名构造函数不能被子类继承。如果想要子类也拥有一个父类一样名字的构造函数,必须在子类是实现这个构造函数。

  • 默认情况下,子类只能调用父类的无名,无参数的构造函数; 父类的无名构造函数会在子类的构造函数前调用; 如果initializer list 也同时定义了,则会先执行initializer list 中的内容,然后在执行父类的无名无参数构造函数,最后调用子类自己的无名无参数构造函数。即下面的顺序:

    • initializer list(初始化列表)
    • super class’s no-arg constructor(父类无参数构造函数)
    • main class’s no-arg constructor (主类无参数构造函数)
  • 如果父类不显示提供无名无参数构造函数的构造函数,在子类中必须手打调用父类的一个构造函数。这种情况下,调用父类的构造函数的代码放在子类构造函数名后,子类构造函数体前,中间使用:(colon)分割。

      class Person {
         String firstName;
      
         Person.fromJson(Map data) {
             print('in Person');
         }
      }
      
      class Employee extends Person {
         // 父类没有无参数的非命名构造函数,必须手动调用一个构造函数     
         super.fromJson(data)
         Employee.fromJson(Map data) : super.fromJson(data) {
            print('in Employee');
         }
      }
      
      main() {
         var emp = new Employee.fromJson({});
      
         // Prints:
         // in Person
         // in Employee
         if (emp is Person) {
           // Type check
           emp.firstName = 'Bob';
         }
         (emp as Person).firstName = 'Bob';
      }  
    

4.初始化列表

  • 除了调用父类的构造函数,也可以通过初始化列表在子类的构造函数体前(大括号前)来初始化实例的变量值,使用逗号,分隔。如下所示:

      class Point {
         num x;
         num y;
      
         Point(this.x, this.y);
      
         // 初始化列表在构造函数运行前设置实例变量。
         Point.fromJson(Map jsonMap)
         : x = jsonMap['x'],
           y = jsonMap['y'] {
            print('In Point.fromJson(): ($x, $y)');
         }
       }  
    

注意:上述代码,初始化程序无法访问 this 关键字。

5.静态构造函数

  • 如果你的类产生的对象永远不会改变,你可以让这些对象成为编译时常量。为此,需要定义一个const构造函数并确保所有的实例变量都是final的。

      class ImmutablePoint {
          final num x;
          final num y;
          const ImmutablePoint(this.x, this.y);
          static final ImmutablePoint origin = const ImmutablePoint(0, 0);
      }  
    

6.重定向构造函数

  • 有时候构造函数的目的只是重定向到该类的另一个构造函数。重定向构造函数没有函数体,使用冒号:分隔。

      class Point {
          num x;
          num y;
      
          // 主构造函数
          Point(this.x, this.y) {
              print("Point($x, $y)");
          }
      
          // 重定向构造函数,指向主构造函数,函数体为空
          Point.alongXAxis(num x) : this(x, 0);
      }
      
      void main() {
          var p1 = new Point(1, 2);
          var p2 = new Point.alongXAxis(4);
      }  
    

7.常量构造函数

  • 如果类的对象不会发生变化,可以构造一个编译时的常量构造函数。定义格式如下:

    • 定义所有的实例变量是final
    • 使用const声明构造函数。

    给个例子:

      class ImmutablePoint {
         final num x;
         final num y;
         const ImmutablePoint(this.x, this.y);
         static final ImmutablePoint origin = const ImmutablePoint(0, 0);
      }  
    

8.工厂构造函数

当实现一个使用factory关键词修饰的构造函数时,这个构造函数不必创建类的新实例。例如,工厂构造函数可能从缓存返回实例,或者它可能返回子类型的实例。 下面的示例演示一个工厂构造函数从缓存返回的对象:

class Logger {
   final String name;
   bool mute = false;

   // _cache 是一个私有库,幸好名字前有个 _ 。 
   static final Map<String, Logger> _cache = <String, Logger>{};

   factory Logger(String name) {
       if (_cache.containsKey(name)) {
          return _cache[name];
       } else {
          final logger = new Logger._internal(name);
          _cache[name] = logger;
          return logger;
       }
    }
    Logger._internal(this.name);

    void log(String msg) {
       if (!mute) {
          print(msg);
       }
    }
 }  

注意:工厂构造函数不能用 this

方法

相信有一点语言基础的都会知道这个东西,所以这里选择从简讨论。

1.实例方法

  • 对象的实例方法可以访问实例变量和this。以下示例中的distanceTo()方法是实例方法的一个例子:

      import 'dart:math';
      
      class Point {
         num x;
         num y;
         Point(this.x, this.y);
      
         num distanceTo(Point other) {
            var dx = x - other.x;
            var dy = y - other.y;
            return sqrt(dx * dx + dy * dy);
         }
       }  
    

2.settersGetters

  • 是一种提供对方法属性读和写的特殊方法。每个实例变量都有一个隐式的getter方法,合适的话可能还会有setter方法。你可以通过实现getterssetters来创建附加属性,也就是直接使用getset关键词:

      class Rectangle {
         num left;
         num top;
         num width;
         num height;
      
         Rectangle(this.left, this.top, this.width, this.height);
      
         // 定义两个计算属性: right and bottom.
         num get right => left + width;
         set right(num value) => left = value - width;
         num get bottom => top + height;
         set bottom(num value) => top = value - height;
      }
      
      main() {
         var rect = new Rectangle(3, 4, 20, 15);
         assert(rect.left == 3);
         rect.right = 12;
         assert(rect.left == -8);
      }  
    
  • 借助于gettersetter,你可以直接使用实例变量,并且在不改变客户代码的情况下把他们包装成方法。

不论是否显式地定义了一个getter,类似增量++的操作符,都能以预期的方式工作。为了避免产生任何向着不期望的方向的影响,操作符一旦调用getter,就会把他的值存在临时变量里。

3.抽象方法

  • Instancegettersetter方法可以是抽象的,也就是定义一个接口,但是把实现交给其他的类。要创建一个抽象方法,使用分号;代替方法体:

       abstract class Doer {
          // ...定义实例变量和方法...
          void doSomething(); // 定义一个抽象方法。
       }
      
       class EffectiveDoer extends Doer {
           void doSomething() {
              // ...提供一个实现,所以这里的方法不是抽象的...
           }
       }  
    

4.枚举类型

  • 枚举类型,通常被称为enumerationsenums,是一种用来代表一个固定数量的常量的特殊类。

  • 声明一个枚举类型需要使用关键字enum

      enum Color {
      	red,
      	green,
      	blue
      }  
    
  • 在枚举中每个值都有一个index getter方法,它返回一个在枚举声明中从 0 开始的位置。例如,第一个值索引值为 0 ,第二个值索引值为 1 。

      assert(Color.red.index == 0);
      assert(Color.green.index == 1);
      assert(Color.blue.index == 2);  
    
  • 要得到枚举列表的所有值,可使用枚举的values常量。

      List<Color> colors = Color.values;
        assert(colors[2] == Color.blue);     
    
  • 可以在switch语句中使用枚举。如果eswitch (e)是显式类型的枚举,那么如果你不处理所有的枚举值将会弹出警告:

      enum Color {
           red,
           green,
           blue
        }
        // ...
        Color aColor = Color.blue;
        switch (aColor) {
            case Color.red:
               print('Red as roses!');
               break;
               
            case Color.green:
               print('Green as grass!');
               break;
          
            default: // Without this, you see a WARNING.
               print(aColor);  // 'Color.blue'
         }  
    

枚举类型有以下限制

  • 你不能在子类中混合或实现一个枚举。
  • 你不能显式实例化一个枚举。

5.为类添加特征:mixins

  • mixins 是一种多类层次结构的类的代码重用。

  • 要使用 mixins ,在with关键字后面跟一个或多个 mixin 的名字。下面的例子显示了两个使用mixins的类:

       class Musician extends Performer with Musical {
            // ...
       }
      
      class Maestro extends Person with Musical, 
          Aggressive, Demented {
      
             Maestro(String maestroName) {
                 name = maestroName;
                 canConduct = true;
             }
       }  
    
  • 要实现 mixin ,就创建一个继承 Object 类的子类,不声明任何构造函数,不调用 super。例如:

       abstract class Musical {
          bool canPlayPiano = false;
          bool canCompose = false;
          bool canConduct = false;
      
         void entertainMe() {
           if (canPlayPiano) {
               print('Playing piano');
           } else if (canConduct) {
               print('Waving hands');
           } else {
               print('Humming to self');
           }
         }
       }  
    

6.类的变量与方法

  • 使用static关键字来实现类变量和类方法。

  • 只有当静态变量被使用时才被初始化。

  • 静态变量, 静态变量(类变量)对于类状态和常数是有用的:

        class Color {
           static const red = const Color('red'); // 一个恒定的静态变量
           final String name;      // 一个实例变量。 
           const Color(this.name); // 一个恒定的构造函数。
        }
      
        main() {
           assert(Color.red.name == 'red');
        }  
    
  • 静态方法, 静态方法(类方法)不在一个实例上进行操作,因而不必访问this。例如:

        import 'dart:math';
      
        class Point {
           num x;
           num y;
           Point(this.x, this.y);
      
           static num distanceBetween(Point a, Point b) {
              var dx = a.x - b.x;
              var dy = a.y - b.y;
              return sqrt(dx * dx + dy * dy);
           }
        }
      
        main() {
          var a = new Point(2, 2);
          var b = new Point(4, 4);
          var distance = Point.distanceBetween(a, b);
          assert(distance < 2.9 && distance > 2.8);
        }  
    

考虑到使用高阶层的方法而不是静态方法,是为了常用或者广泛使用的工具和功能。

  • 你可以将静态方法作为编译时常量。例如,你可以把静态方法作为一个参数传递给静态构造函数。

未完待续

发布了5 篇原创文章 · 获赞 0 · 访问量 59

猜你喜欢

转载自blog.csdn.net/weixin_43954609/article/details/103618176
今日推荐