Расширения класса

Последнее обновление: 08.04.2023

Расширения класса позволяют добавить функциональность в уже имеющийся класс. Это может быть полезно, если мы используем класс и хотим добавить в него некоторую функциональность, однако исходный код этого класса нам недоступен. Общий синтаксис расширения класса:

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
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850