Операторы проверки типов в Dart

Обзор трёх операторов (as /is/is!)

ОператорОписание работы
asПринудительное преобразование типа; также используется для задания псевдонима библиотеки
isПроверка типа: возвращает true, если объект относится к указанному типу
is!Проверка типа: возвращает true, если объект не относится к указанному типу

Условие истинности выражения obj is T: объект obj реализует или наследует тип T (включая родительские классы, интерфейсы и нулевые типы).

Пример: выражение Любой объект is Object? всегда даёт true.

Оператор is

Безопасная проверка типа

Проверяет тип объекта во время выполнения программы; если результат равен true, внутри блока кода объект можно использовать как соответствующий тип без ручного преобразования. Если объект равен null или типы не совпадают, возвращается false, исключений не возникает.

В примере ниже используются объекты классов. Если вы ещё не изучали классы, считайте их шаблонами для создания данных.

class Person {
  String firstName = '';
}

class Employee extends Person {}

void main() {
  dynamic employee = Employee();

  // Проверка типа через is
  if (employee is Person) {
    // При входе в ветвь переменная автоматически распознаётся как Person
    employee.firstName = "Jack";
    print(employee.firstName); // Выведет Jack
  }

  dynamic emptyObj = null;
  if (emptyObj is Person) {
    // Код не выполнится, null не соответствует типу Person
    print("Совпадение найдено");
  }
}Code language: Dart (dart)

В данном коде определён класс Person, а затем наследующий его класс Employee.

В коде оператор is проверяет, является ли объект экземпляром Person. При истинном результате внутри фигурных скобок объект используется напрямую как Person. Это возможно благодаря динамическому типу в записи dynamic employee = Employee();.

Во втором примере dynamic emptyObj = null; переменная хранит значение null, поэтому выражение emptyObj is Person даёт false, код внутри блока не запустится.

Оператор is!

Обратная проверка типа

Синтаксис:

переменная is! тип

Фактически это то же самое, что инверсия результата оператора is.

Эквивалент записи !(переменная is тип), проверяет, что объект не является целевым типом.

Рассмотрим пример:

class Person {
  String name = '';
}

void main() {
  dynamic obj = "текст";
  if (obj is! Person) {
    print("obj не относится к типу Person");
  }
}Code language: Dart (dart)

В данном примере выполнится код внутри блока и выведется строка «obj не относится к типу Person», потому что obj не является экземпляром, созданным по шаблону Person.

Оператор as

Принудительное преобразование типа

Способ применения:

(переменная as целевой_тип).свойство/методCode language: JavaScript (javascript)

Используйте этот оператор только если вы на 100% уверены, что объект имеет нужный тип. В противном случае во время выполнения возникнет исключение. Для устойчивости программы не рекомендуется частое использование as.

class Teacher {
  String name = '';
}

void main() {
  dynamic jason = Teacher();
  // Принудительное преобразование в Teacher для доступа к свойству
  (jason as Teacher).name = "Jason";
  print((jason as Teacher).name); // Jason
}Code language: Dart (dart)

Результат вывода примера — строка Jason

Мы напрямую создаём экземпляр Teacher через dynamic jason = Teacher();, поэтому точно знаем, что переменная имеет тип Teacher, ошибок не возникнет.

Если написать код ниже, появится ошибка.

class Teacher {
  String name = '';
}

class Car {
  String name = '';
}

void main() {
  dynamic jason = Teacher();
  
  // Принудительное преобразование в Car для доступа к свойству
  (jason as Car).name = "Jason";//❌ошибка
  print((jason as Car).name); //❌ошибка
}Code language: Dart (dart)

Ошибка возникает, потому что изначально jason — объект Teacher, но мы пытаемся принудительно преобразовать его в тип Car.

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Unhandled exception:
type 'Teacher' is not a subtype of type 'Car' in type cast
#0      main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:12:10)
#1      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#2      _RawReceivePort._handleMessage (dart:isolate-patch/isolate-patch.dart:193:12)Code language: PHP (php)

Основное сравнение as и is

// Вариант 1: принудительное преобразование через as
(employee as Person).firstName = 'Bob';

// Вариант 2: предварительная проверка через is перед использованием
if (employee is Person) {
  employee.firstName = 'Bob';
}Code language: JavaScript (javascript)

Поведение при employee равном null или не являющемся объектом типа Person:

  • Вариант с as: выбрасывается исключение CastError, программа аварийно завершается;
  • Вариант с is: ветвь if не выполняется, ошибок нет, никаких действий не происходит.

Хотя код получается немного длиннее, предпочитайте проверку через is — это повышает устойчивость программы.

Второе назначение оператора as

Второе назначение оператора as

as используется не только для преобразования типов: при импорте библиотек с его помощью задают псевдоним библиотеки

import 'dart:math' as math;

void main() {
  // Обращение к классам и методам через псевдоним библиотеки
  print(math.pi); // Выведет 3.141592653589793
}Code language: JavaScript (javascript)

Итоговый полный пример

class Animal {}
class Cat extends Animal {}

void main() {
  dynamic obj = Cat();

  // 1. Проверка через is
  if (obj is Animal) {
    print("obj относится к типу Animal");
  }

  // 2. Обратная проверка через is!
  if (obj is! String) {
    print("obj не является строкой");
  }

  // 3. Принудительное преобразование через as
  Animal a = obj as Animal;

  // Ошибочный пример, вызывающий сбой программы
  dynamic str = "hello";
  // Animal error = str as Animal; // Возникает CastError
}Code language: JavaScript (javascript)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *