Расширения класса позволяют добавить функциональность в уже имеющийся класс. Это может быть полезно, если мы используем класс и хотим добавить в него некоторую функциональность, однако исходный код этого класса нам недоступен. Общий синтаксис расширения класса:
extension on имя_класса { // код расширения }
После слов extension on указывается имя класса, для которого определяется расширение. Далее в фигурных скобках идет собственно код расширения.
Например, добавим для встроенного типа int метод, который возводит число в определенную степень:
// расширение для класса int extension on int { double pow(int n) { // метод возведения в степень double result = 1; for (int i=0; i < n.abs(); i++) { result *= this; } if(n < 0) result = 1 / result; return result; } } void main (){ int number = 2; print(number.pow(2)); // 4.0 print(number.pow(-2)); // 0.25 print(number.pow(3)); // 8 print(number.pow(-3)); // 0.125 }
В данном случае расширение класса int содержит один метод - pow, который в качестве параметра принимает степень числа и с помощью цикла возводит число в данную степень.
Причем степерь может быть как положительной, так и отрицательной. Поэтому получаем абсолютное значение степени по модулю с помощью метода abs()
, и пока счетчик не дойдет до абсолютного значения степени,
последовательно умножаем переменную result на текущее число:
for (int i=0; i < n.abs(); i++) { result *= this; }
Причем текущее число - текущий объект, на котором будет вызываться этот метод и который надо возвести в степень n, доступен через this.
После этого мы сможем вызывать метод pow, как и другие стандартные встроенные методы класса int:
int number = 2; print(number.pow(2)); // 4.0
Подобным образом мы можем определять и свойства с геттерами и сеттерами:
// расширение для класса int extension on int { int get square => this * this; // квадрат числа int get cube => this * this * this; // число в кубе double pow(int n) { double result = 1; for (int i=0; i < n.abs(); i++) { result *= this; } if(n < 0) result = 1 / result; return result; } } void main (){ int number = 2; print(number.square); // 4 print(number.cube); // 8 }
Другой пример:
extension on String { bool get isEmptyOrWhiteSpace => this.trim().length == 0; } void main (){ String text = " "; print(text.isEmptyOrWhiteSpace); // true text = ""; print(text.isEmptyOrWhiteSpace); // true text = "h"; print(text.isEmptyOrWhiteSpace); // false }
Для проверки строки на пустоту класс String определяет поле isEmpty - если строка пуста, то оно возвращает true
. Однако если строка содержит только
пробелы, то такая строка формально не пуста, и поле isEmpty
возвратит false. И чтобы строка с одними пробелами также считалась пустой, определяем дополнительный геттер - isEmptyOrWhiteSpace:
bool get isEmptyOrWhiteSpace => this.trim().length == 0;
К текущей строке применяем метод trim(), который из текущей строки удаляет начальные и конечные пробелы и возвращает результат в виде новой строки. И если длина этой новой строки равна 0, то значит текущая строка пуста или состоит из одних пробелов.
Более сложный пример:
extension on String { String get encoded =>_code(1); String get decoded =>_code(-1); String _code(int step) { final output = StringBuffer(); for (final codePoint in runes) { output.writeCharCode(codePoint + step); } return output.toString(); } } void main (){ String text = "Hello"; String secret = text.encoded; print(secret); // Ifmmp String original = secret.decoded; print(original); // Hello }
Здесь определены расширения для класса String. Эти расширения представляют геттеры encoded - зашифрованную строку и decoded - расшифрованную строку. Реальную же работу выполняет приватный метод _code(). Он принимает параметр step, который определяет, как надо сдвинуть кодовые точки внутри строки.
Поскольку строка сама по себе представляет неизменяемый тип, то для операций со строкой определяется объект StringBuffer - некоторый строковый буфер:
final output = StringBuffer();
С помощью цикла for-in проходим по всем символам текущей строки, которые можно получить с помощью поля runes:
for (final codePoint in runes) { output.writeCharCode(codePoint + step); }
С помощью метода output.writeCharCode()
добавляем в строковый буфер один символ. Все символы фактически представляют числовые символы.
И здесь мы заносим числовой код символа, сдвинутый на 1 позицию. Если step - положительное число, то сдвиг идет вперед. То есть
если символ "A", то в буфер заносится символ "B". Если символ - "B", то в буфер заносится символ "C". Если step - отрицательное число, то сдвиг идет назад.
В конце с помощью метода toString у строкового буфера можно получить строку, которая возвращается в качестве результата метода.
Затем в программе, используя свойства encoded и decoded, мы можем получить из строки зашифрованное и расшифрованное сообщение:
String text = "Hello"; String secret = text.encoded; print(secret); // Ifmmp String original = secret.decoded; print(original); // Hello