Introduction
To begin with, what is extension in general? Extension is syntactic sugar that extends an existing class in a place different from the class declaration module.
In programming, extension methods have been around for a long time, so they got to dart. Extension is actively used in languages โโsuch as C #, Java via Manifold, Swift, Kotlin and many others.
Problem
Let's say we have a catchError method, which is just awful and needs to be rewritten to a new cool function. Suppose that he uses a function of any type as an argument, instead of a strictly typed function or a function type check, and this happens because 8 months ago when developing this functionality, it was logical at that time.
The first thing that comes to mind is to rewrite this function, but here we are faced with the problem that it occurs too often in the project, and changing the function will lead to the inoperability of the entire project.
Well, if the first option is not for us. suitable, for logical reasons, then I can implement a new Future function that will meet all my requirements.
abstract class Future<T> { ... /// Catches any [error] of type [E]. Future<T> onError<E>(FutureOr<T> handleError(E error, StackTrace stack)) => this.catchError(... - ...); } ... }
and Iโll call her like this:
Future<String> someString = ...; someString.onError((FormatException e, s) => ...).then(...);
Unfortunately, I cannot add this function to the Future class. If I do this, I will also add it to the Future interface, and any other class that implements this interface will be incomplete and will no longer compile.
Well, another option is to implement a third-party function that will look like this:
Future<T> onFutureError<T, E>(Future<T> source, FutureOr<T> handleError(E error, StackTrace stack)) => source.catchError(... - ...);
And her call would look like this:
Future<String> someString = ...; onFutureError(someString, (FormatException e, s) => ...).then(...);
Great, everything works! But itโs sad that it began to be terribly read. We use methods. which are implemented inside the class, so they are called -.doingSomething (); This code is understandable, I just read it from left to right and stand in my head a sequence of events. Using a helper function makes the code cumbersome and less readable.
Well then, I can implement a new class and let users wrap their old interface with improved functionality.
class CustomFuture<T> { CustomFuture(Future<T> future) : _wrapper = future; Future<T> _wrapper; Future<T> onError<E>(FutureOr<T> handleError(E error, StackTrace stack)) => _wrapper.catchError(...- ...); }
and the call will look like this:
Future<String> someString = ...; CustomFuture(someString).onError((FormatException e, s) => ...).then(...);
Looks great!
Solving a problem with extension
As soon as we stop programming in pascal and return to 2019, the implementation of this functionality will be reduced to this size:
extension CustomFuture <T> on Future<T> { Future<T> onError<E>( FutureOr<T> handleError(E error, StackTrace stack)) => this.catchError(...something clever...); }
and this is what the call will look like:
Future<String> someString = ...; someString.onError((FormatException e, s) => ...).then(...);
That's all! The solution to this problem took only 5 lines of code. You. You may wonder what kind of magic is and how it works.
In fact, it behaves just like the wrapper class, although in reality it is just an auxiliary static function. Extension allows you to let go of explicit wrapper writing.
This is not a wrapper
The extension design works in such a way that it looks like a declaration of an existing class, but acts as if it were a wrapper with a private _wrapper. But there is one advantage compared to the wrapper class, this is accessing the class itself directly, rather than accessing the _wrapper wrapper class.
This feature was not made for the sake of features, but as I said earlier, extensions are indeed a more convenient way to call static functions. This means that there is no wrapper object.
It's All Static
I said โstatic extension methodsโ above, and I did it for a reason!
Dart is statically typed. The compiler knows the type of each expression at compile time, so if you write user.age (19) and age is an extension, then the compiler must figure out what type is wrapped in the given object in order to find the type of the whole call.
What problems can arise?
The simplest example of problems with extension is when you have more than one extension in its scope. Basically, the winner is the extension closest to the actual type of expression that you are calling the member, with some reservations.
The easiest way to solve the problem is to connect strictly the extension you need, or you can use the extension explicitly:
... List list = ...; MyList(list).printlist(); SomeList(list).printlist(); ... extension MyList on List { void printlist() { print(...- ...); } } extension SomeList on List { void printlist() { print(...- ...); } }
Summary
- The dart language has a convenient tool for expanding existing functionality.
- You can extend methods, operators, setters and getters, but not fields.
- You can invoke extension methods explicitly or - when there is no conflict with an interface member or other extension-implicitly.
- Implicit calls work just like explicit calls.
- Extensions are static. Everything about them is solved on the basis of static types.
If the output of the extension fails due to conflicting extensions, then you can do one of the following:
- Apply the extension explicitly.
- Import the conflicting extension with the prefix, because then it is not available for implicit calling.
- Do not import a conflicting extension at all.
That's all! You can use extension to its full potential.
And of course, useful links:
Website flutter
Dart website
Where can I read more about extension
Telegram channel where I talk about all the newest in the world of Flutter and not only