The author | | vlad source vlad (public number: fulade_me)

class

Dart is an object-oriented language. All objects are instances of a class, and all classes inherit from the Object class. Every class other than Object has only one superclass, and the code of a class can be reused in multiple other class inherits.

Class instance variable

Here is an example of declaring an instance variable:

class Point {
  double x; // Declare the double variable x and initialize it to null.
  double y; // Declare the double variable y and initialize it to null
  double z = 0; // declare the double variable z and initialize to 0.
}
Copy the code

All uninitialized instance variables have a value of NULL.

All instance variables implicitly declare a Getter method, and non-final instance variables implicitly declare a Setter method

class Point {
  double x;
  double y;
}

void main() {
  var point = Point();
  point.x = 4; // Use Setter methods for x.
  assert(point.x == 4); // Use the Getter method for x.
  assert(point.y == null); // Default value is null.
}
Copy the code

If you initialize an instance variable when you declare it (rather than in a constructor or other method), the value of the instance variable is set when the object instance is created, before the constructor and its initializer list are executed.

Access a member of the class

The members of an object consist of functions and data (that is, methods and instance variables). Method calls are made through the object, which has access to its functions and data. To access an instance variable or method of an object using. :

var p = Point(2.2);
// Get the y value
assert(p.y == 2);
// Call the distanceTo() method on p.
double distance = p.distanceTo(Point(4.4));
Copy the code

Use? . Instead of. Can avoid the problem caused by the null expression on the left side:

// If p is non-null, set a variable equal to its y value.
vara = p? .y;Copy the code
methods

Methods are functions that provide behavior for an object.

Object instance methods have access to instance variables and this. The distanceTo() method below is an example of an instance method:

import 'dart:math';

class Point {
  double x, y;

  Point(this.x, this.y);

  double distanceTo(Point other) {
    var dx = x - other.x;
    var dy = y - other.y;
    returnsqrt(dx * dx + dy * dy); }}Copy the code
Overriding class member

Subclasses can override instance methods (including operators), getters, and setters of their parent classes. You can use the @override annotation to indicate that you have overridden a member:

class SmartTelevision extends Television {
  @override
  voidturnOn() {... }/ /...
}
Copy the code
NoSuchMethod method

If calling a method or instance variable that does not exist on the object will trigger the noSuchMethod method, you can override the noSuchMethod method to track and record this behavior:

class A {
  // Unless you override noSuchMethod, calling a nonexistent member will cause NoSuchMethodError.
  @override
  void noSuchMethod(Invocation invocation) {
    print('You tried using a non-existent member:' + '${invocation.memberName}'); }}Copy the code

You cannot call an unimplemented method unless one of the following conditions is met:

  • The receiver is staticdynamicType.
  • The receiver has static typing, defines unimplemented methods, and the receiver’s dynamic typing is implementednoSuchMethodMethod and concrete implementation withObjectThe difference between.

Class static variables and static methods

The static keyword is used to declare class variables or class methods.

A static variable

Static variables (class variables) are often used to declare state variables and constants that belong within the scope of a class:

class Queue {
  static const initialCapacity = 16;
  / /...
}
void main() {
  assert(Queue.initialCapacity == 16);
}
Copy the code

Static variables are initialized the first time they are used.

A static method

Static methods (that is, class methods) cannot be accessed by an instance of a class. Likewise, the keyword this cannot be used within static methods:

import 'dart:math';
class Point {
  double x, y;
  Point(this.x, this.y);
  static double distanceBetween(Point a, Point b) {
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    returnsqrt(dx * dx + dy * dy); }}void main() {
  var a = Point(2.2);
  var b = Point(4.4);
  var distance = Point.distanceBetween(a, b);
  assert(2.8 < distance && distance < 2.9);
  print(distance);
}
Copy the code

Use constructors to build objects

Constructors can be used to create an object. Constructors can be named by either class name or class name. The form of an identifier. For example, the following code uses the Point() and point.fromjson () constructors to create a Point object:

var p1 = Point(2.2);
var p2 = Point.fromJson({'x': 1.'y': 2});
Copy the code

The following code has the same effect. The new keyword before the constructor name is optional:

var p1 = new Point(2.2);
var p2 = new Point.fromJson({'x': 1.'y': 2});
Copy the code

Some classes provide constant constructors. Use the constant constructor to create compile-time constants preceded by the const keyword:

var p = const ImmutablePoint(2.2);
Copy the code

Two compile-time constants constructed with the same constructor and the same parameter values are the same object:

var a = const ImmutablePoint(1.1);
var b = const ImmutablePoint(1.1);

assert(identical(a, b)); // They are the same instance!
Copy the code

You can omit the const keyword before constructors or literals, depending on the situation in which constant context is used. For example, in the following example we create a constant Map:

// There are many const keywords here
const pointAndLine = const {
  'point': const [const ImmutablePoint(0.0)].'line': const [const ImmutablePoint(1.10), const ImmutablePoint(2 -.11)]};Copy the code

Depending on the context, you can keep only the first const keyword and omit all the rest:

// Only one const keyword is required; the others are implicitly related to context.
const pointAndLine = {
  'point': [ImmutablePoint(0.0)].'line': [ImmutablePoint(1.10), ImmutablePoint(2 -.11)]};Copy the code

However, if it is not possible to omit const from the context, the const keyword should not be omitted. Otherwise, a nonconstant object will be created. For example:

var a = const ImmutablePoint(1.1); // Creates a constant
var b = ImmutablePoint(1.1); // a constant is not created
assert(! identical(a, b));// These two variables are not identical
Copy the code

Gets the type of the object

The Type of an Object, which is an instance of Type, can be obtained at runtime using the runtimeType property of an Object.

var = Point(2.2);
print('The type of a is ${a.runtimeType}');
Copy the code

The constructor

A constructor can be declared by declaring a function that has the same name as the class (you can also add additional identifiers to the named constructor). Most constructors take the form of generative constructors, which create an instance of a class:

class Point {
  double x, y;
  Point(double x, double y) {
    // There will be a better way to implement this logic, stay tuned.
    this.x = x;
    this.y = y; }}Copy the code

Use the this keyword to reference the current instance. Assigning a value to an instance variable in a constructor is similar for most programming languages, but Dart provides a special syntactic sugar to simplify this step:

class Point {
  double x, y;
  // The syntax sugar used to set x and y before the constructor body is executed.
  Point(this.x, this.y);
}
Copy the code
Default constructor

If you don’t declare a constructor, Dart automatically generates a no-argument constructor that calls its parent’s no-argument constructor.

Constructors are not inherited

A subclass does not inherit the constructor of its parent class, and if the subclass does not declare a constructor, there is only one constructor with no arguments by default.

The named constructor

You can declare multiple named constructors for a class to express more explicit intent:

class Point {
  double x, y;

  Point(this.x, this.y);

  // the named constructor
  Point.origin() {
    x = 0;
    y = 0; }}Copy the code

Constructors are uninheritable, which means that a subclass cannot inherit the parent class’s named constructor. If you want to provide a named constructor in a subclass with the same name as the parent class’s named constructor, you need to declare it explicitly in the subclass.

Call the parent class non-default constructor

By default, the subclass constructor invokes the anonymous no-arg constructor of the parent, and the call will be in front of the subclass constructor function body code execution, if there is a list of initialization subclass constructor, then the initialization list before invoking the constructor of the parent is carried out, in general, the three call sequence is as follows:

  1. Initialization list
  2. A parameterless constructor for the parent class
  3. Constructor of the current class

If the parent class does not have an anonymous no-argument constructor, then the subclass must call one of the constructors of the parent class. Specifying a parent constructor for the subclass’s constructor is used only in front of the constructor body: specify.

Since the argument is passed to the parent constructor before the subclass constructor is executed, the argument can also be an expression, such as a function:

class Employee extends Person {
  Employee() : super.fromJson(defaultData);
  / /...
}
Copy the code
Initialization list

In addition to calling the superclass constructor, instance variables can be initialized before the constructor body is executed. Each instance variable is separated by a comma.

// Use the initializer list to set the instance variable before the constructor body is executed.
Point.fromJson(Map<String.double> json)
    : x = json['x'],
      y = json['y'] {
  print('In Point.fromJson(): ($x.$y) ');
}
Copy the code

In development mode, you can use assert in the initialization list to validate input data:

Point.withAssert(this.x, this.y) : assert(x >= 0) {
  print('In Point.withAssert(): ($x.$y) ');
}
Copy the code
Redirect constructor

The redirection constructor does not have a function body and is used only after the function is signed: specifies the other constructor to be redirected to:

class Point {
  double x, y;

  // The primary constructor of the class.
  Point(this.x, this.y);

  // Delegate implementation to the main constructor.
  Point.alongXAxis(double x) : this(x, 0);
}
Copy the code
Constant constructor

If the objects generated by the class are immutable, you can make them compile-time constants when they are generated. You can do this by prefixing the class constructor with the const keyword and making sure that all instance variables are final.

class ImmutablePoint {
  static final ImmutablePoint origin =
      const ImmutablePoint(0.0);

  final double x, y;

  const ImmutablePoint(this.x, this.y);
}
Copy the code

Instances created by constant constructors are not always constants.

Factory constructor

Identifying the constructor of a class with the factory keyword makes the constructor a factory constructor, which means that using the constructor to construct instances of the class does not always return a new instance object. For example, a factory constructor might return an instance from the cache, or an instance of a subtype.

In the following example, the Logger factory constructor returns an object from the cache, and the Logger.fromjson factory constructor initializes a final variable from the JSON object.

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

  // the _cache variable is library private because it is preceded by an underscore.
  static final Map<String, Logger> _cache =
      <String, Logger>{};

  factory Logger(String name) {
    return _cache.putIfAbsent(
        name, () => Logger._internal(name));
  }

  factory Logger.fromJson(Map<String.Object> json) {
    return Logger(json['name'].toString());
  }

  Logger._internal(this.name);

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

The factory constructor is called like any other constructor:

var logger = Logger('UI');
logger.log('Button clicked');

var logMap = {'name': 'UI'};
var loggerJson = Logger.fromJson(logMap);
Copy the code

An abstract class

Using the abstract keyword to identify a class makes it an abstract class that cannot be instantiated. Abstract classes are often used to declare interface methods and sometimes have concrete method implementations. If you want an abstract class to be instantiated at the same time, you can define a factory constructor for it.

Abstract classes often contain abstract methods. Here is an example of an abstract class that declares an abstract method:

// The class is declared abstract, so it cannot be instantiated.
abstract class AbstractContainer {
  // Define constructors, fields, methods, etc...
  void updateChildren(); // Abstract methods.
}
Copy the code

Implicit interface

Each class implicitly defines and implements an interface that contains all instance members of the class and other interfaces that the class implements. If you want to create A class A API that supports calling class B without inheriting from class B, you can implement A class B interface.

A class implements one or more interfaces and implements the API defined by each interface using the keyword implements:

The Person class contains the greet() method in its implicit interface.
class Person {
  // the _name variable is also included in the interface, but it is only visible in the library.
  final _name;

  // The constructor is not in the interface.
  Person(this._name);

  // Greet () on the interface.
  String greet(String who) => 'hello,$who. I am a$_name. ';
}

// An implementation of the Person interface
class Impostor implements Person {
  get _name => ' ';

  String greet(String who) => 'how are you$who. Do you know who I am? ';
}

String greetBob(Person person) => person.greet('small fang');

void main() {
  print(greetBob(Person('the little non-success')));
  print(greetBob(Impostor()));
}
Copy the code

If you need to implement multiple interface classes, you can separate each interface class with a comma:

class Point implements Comparable.Location {}Copy the code

Extending a class

Create a subclass using the extends keyword and reference a parent class using the super keyword:

class Television {
  voidturnOn() { _illuminateDisplay(); _activateIrSensor(); }}class SmartTelevision extends Television {
  void turnOn() {
    super.turnOn(); _bootNetworkInterface(); _initializeMemory(); _upgradeApps(); }}Copy the code

Use mixins to add functionality to classes

Mixin is a method pattern for reusing code from a class in multiple inheritance. Use the Mixin pattern using the with keyword followed by the name of the Mixin class:

class Musician extends Performer with Musical {
  / /...
}

class Maestro extends Person
    with Musical.Aggressive.Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true; }}Copy the code

Define a class that inherits from Object and does not define a constructor for the class. This class is a Mixin class. Unless you want the class to be used as a normal class, you can use the keyword Mixin instead of class to make it a pure Mixin class:

mixin 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'); }}}Copy the code

You can use the keyword on to specify which classes can use this Mixin class. For example, if there is A Mixin class A, but A can only be used by class B, you can define ‘A’ like this:

class Musician {
  // ...
}
mixin MusicalPerformer on Musician {
  // ...
}
class SingerDancer extends Musician with MusicalPerformer {
  // ...
}
Copy the code

The Extension method

The Extension method, introduced in Dart 2.7, is a way to add functionality to an existing library. Here is an example of using the extension method in String, which we call parseInt() and which is defined in string_apis. Dart:

extension NumberParsing on String {
  int parseInt() {
    return int.parse(this);
  }

  double parseDouble() {
    return double.parse(this); }}Copy the code

Then use the parseInt() method in string_apis. Dart

import 'string_apis.dart';
print(The '42'.padLeft(5)); // Use a String method.
print(The '42'.parseInt()); // Use an extension method.
Copy the code