The use of abstract classes
Dart abstract classes can declare only methods or have concrete method implementations, but cannot be directly used to create instances. They can only be inherited or used as interfaces.
Define an abstract class Animal
abstract class Animal {
// Declare only the eat method
void eat();
// Declare a method with an implementation
void sleep() {
print("Sleep"); }}Copy the code
Continued use
class Cat extends Animal {
@override
void eat() {
print("Meow meow eat.");
sleep();
}
// The sleep method is not implemented
}
Copy the code
ACTS as an interface
class Cat implements Animal {
void eat() {
print("Eat");
}
// The sleep method must be implemented
void sleep() {
print('sleep'); }}Copy the code
instantiation
final animal = Animal();
// Instantiation of an abstract class fails
// Error: The class 'Test' is abstract and can't be instantiated.
Copy the code
- Abstract classes cannot be instantiated.
- Inheritance: Subclasses compare and implement abstract methods, and subclasses may not override methods already implemented in abstract classes.
- Interface: All methods declared in an abstract class must be implemented
Instantiation of abstract classes
Dart maps and Lists are abstract classes that can be used to create instance objects
final list = List(a);final dict = Map<String.dynamic> ();Copy the code
Let’s look at the Map source code:
Map is an abstract class, but notice at this point that in this abstract class, Map defines a factory constructor. This is the key to making the abstract class instantiable, because the factory method can return an instance object, but that object doesn’t have to be of the same type as the current class!
There is no concrete implementation of Map’s factory method at this point, just a keyword external in front of the factory constructor. The external keyword separates the declaration of a method from the implementation. That is, the implementation of a method can be done for us by the external. How can the external be associated with the declared method? This is where the @patch annotation is needed to bind the external method implementation to the declared method
external
Method declaration and implementation can be separated@patch
To associate with a classexternal
The implementation of a modified method
You can find the Map implementation source code according to the following path
// flutter/bin/cache/dart-sdk/lib/_internal/vm/lib/map_patch.dart
@patch
factory Map() = >new LinkedHashMap<K, V>();
Copy the code
As you can see, LinkedHashMap is used to implement the Map.
Let’s take a look at the source code of the LinkedHashMap implementation as follows:
// flutter/bin/cache/dart-sdk/lib/collection/linked_hash_map.dart
external factory LinkedHashMap(
{bool Function(K, K)? equals,
int Function(K)? hashCode,
bool Function(dynamic)? isValidKey});
Copy the code
Here again we see that LinkedHashMap is just a declaration, finding an implementation
// flutter/bin/cache/dart-sdk/lib/_internal/vm/lib/collection_patch.dart
@patch
class LinkedHashMap<K.V> {
@patch
factory LinkedHashMap(
{boolequals(K key1, K key2)? .inthashCode(K key)? .boolisValidKey(potentialKey)? {})if (isValidKey == null) {
if (hashCode == null) {
if (equals == null) {
return new _InternalLinkedHashMap<K, V>();
}
hashCode = _defaultHashCode;
} else {
if (identical(identityHashCode, hashCode) &&
identical(identical, equals)) {
return new _CompactLinkedIdentityHashMap<K, V>();
}
equals ??= _defaultEquals;
}
} else{ hashCode ?? = _defaultHashCode; equals ?? = _defaultEquals; }return new_CompactLinkedCustomHashMap<K, V>(equals, hashCode, isValidKey); }... }Copy the code
As you can see, the LinkedHashMap () method returns an instance of the factory structure type is _InternalLinkedHashMap or _CompactLinkedCustomHashMap, here we’ll have a look at these two classes of realize the source code
// flutter/bin/cache/dart-sdk/lib/_internal/vm/lib/compact_hash.dart
@pragma("vm:entry-point")
class _InternalLinkedHashMap<K.V> extends _HashVMBase
with
MapMixin<K.V>,
_LinkedHashMapMixin<K.V>,
_HashBase._OperatorEqualsAndHashCode
implements LinkedHashMap<K.V> {
_InternalLinkedHashMap() {
_index = new Uint32List(_HashBase._INITIAL_INDEX_SIZE);
_hashMask = _HashBase._indexSizeToHashMask(_HashBase._INITIAL_INDEX_SIZE);
_data = new List.filled(_HashBase._INITIAL_INDEX_SIZE, null);
_usedData = 0;
_deletedKeys = 0; }}...class _CompactLinkedIdentityHashMap<K.V> extends _HashFieldBase
with
MapMixin<K.V>,
_LinkedHashMapMixin<K.V>,
_HashBase._IdenticalAndIdentityHashCode
implements LinkedHashMap<K.V> {
_CompactLinkedIdentityHashMap() : super(_HashBase._INITIAL_INDEX_SIZE);
}
class _CompactLinkedCustomHashMap<K.V> extends _HashFieldBase
with MapMixin<K.V>, _LinkedHashMapMixin<K.V>, _HashBase
implements LinkedHashMap<K.V> {
final _equality;
final _hasher;
final _validKey;
// TODO(koda): Ask gbracha why I cannot have fields _equals/_hashCode.
int _hashCode(e) => _hasher(e);
bool _equals(e1, e2) => _equality(e1, e2);
bool containsKey(Object? o) => _validKey(o) ? super.containsKey(o) : false;
V? operator[] (Object? o) => _validKey(o) ? super[o] : null;
V? remove(Object? o) => _validKey(o) ? super.remove(o) : null;
_CompactLinkedCustomHashMap(this._equality, this._hasher, validKey) : _validKey = (validKey ! =null)? validKey :new _TypeTest<K>().test,
super(_HashBase._INITIAL_INDEX_SIZE);
}
Copy the code
They are normal classes with no factory constructors, that is, external Factory Map() in Map; Eventually returned to the final instance types for _InternalLinkedHashMap or _CompactLinkedCustomHashMap
We can do a simple test
final map = Map(a);print(map.runtimeType);
// Print the result
// _InternalLinkedHashMap<dynamic, dynamic>
Copy the code
Let’s try instantiating an abstract class
abstract class Animal {
void eat();
void sleep() {
print("Sleep");
}
factory Animal() {
returnCat(); }}class Cat implements Animal {
void eat() {
print("Eat");
}
void sleep() {
print('sleep'); }}Copy the code
final animal = Animal();
print(animal.runtimeType);
// Print the result: Cat
Copy the code
Now, some of you might want to ask, well, this is interface, can we use inheritance? A factory constructor defined in an abstract class cannot be defined in a subclass other than the factory constructor
To sum up:
Abstract classes cannot be instantiated directly, but they can be instantiated indirectly by implementing the factory constructor!
Third, add
So why not just make it happen when you announce it? 🤔 The advantages of this are:
- Declarations that reuse the same SET of apis
- Different implementations can be made for different platforms
Different implementations for different platforms can be seen in the source code provided below
// flutter/bin/cache/dart-sdk/lib/io/file_system_entity.dart
abstract class _FileSystemWatcher {
external static Stream<FileSystemEvent> _watch(
String path, int events, bool recursive);
external static bool get isSupported;
}
Copy the code
// flutter/bin/cache/dart-sdk/lib/_internal/vm/bin/file_patch.dart
@patch
static Stream<FileSystemEvent> _watch(
String path, int events, bool recursive) {
if (Platform.isLinux) {
return new _InotifyFileSystemWatcher(path, events, recursive)._stream;
}
if (Platform.isWindows) {
return new _Win32FileSystemWatcher(path, events, recursive)._stream;
}
if (Platform.isMacOS) {
return new _FSEventStreamFileSystemWatcher(path, events, recursive)
._stream;
}
throw new FileSystemException(
"File system watching is not supported on this platform");
}
Copy the code