Ошибка Error: A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't

void main() {
  var bob = User('Bob', 20, true, ['footbal', 'Skate']);
//    ..name = 'Bob'
//    ..age = 40;
 bob.info();
  
  var alex = User('Alex', 25, false, ['Basketbol']);
}

class User{
  String? name;
  int? age;
  bool? isHappy;
  List<String>? hobbies;
   
  User(name, age, [ishappy, hobbies]){
    this.name = name;
    this.age = age;
    this.isHappy = isHappy;
    this.hobbies = hobbies;
    
  }
  
  void info(){
    var happy = isHappy ? 'happy' : 'not happy';
    print('User $name is $age years old. He is $happy. His hobbies:');
    for(var el in hobbies){
      print(el);
    }
  }
}

ошибка:

Error compiling to JavaScript:
Info: Compiling with sound null safety
lib/main.dart:25:17:
Error: A value of type 'bool?' can't be assigned to a variable of type 'bool' because 'bool?' is nullable and 'bool' isn't.
    var happy = isHappy ? 'happy' : 'not happy';
                ^
lib/main.dart:27:19:
Error: The type 'List<String>?' used in the 'for' loop must implement 'Iterable<dynamic>' because 'List<String>?' is nullable and 'Iterable<dynamic>' isn't.
 - 'List' is from 'dart:core'.
 - 'Iterable' is from 'dart:core'.
    for(var el in hobbies){
                  ^
Error: Compilation failed.

делаю код тут: https://dartpad.dartlang.org/?

Я в программировании зеленый, подскажите, пожалуйста, в чем может быть проблема.


Ответы (1 шт):

Автор решения: Михаил Ребров

В чем ошибка?

var happy = isHappy ? 'happy' : 'not happy';

В сообщении об ошибке говориться о том, что компилятор не может привести переменную типа bool? к bool
И если вы заметили то в списке полей у Вас указан тип с вопросительным знаком:

bool? isHappy;

Что это вопросительный знак значит?
Данный знак что данная переменная опциональная.
Это значит что

  • она может быть и тогда в ней будет значение типа bool,
  • а может не быть и тогда в ней будет значение null

Т.е. она может принимать одно из трех значений:

  • true
  • false
  • null

Но, это не входит в планы остальных языковых конструкций.

  • Они знают как себя вести с true
  • Они знают как себя вести с false
  • А что делать когда у тебя null они не знают

Подробнее узнать об этом можно в оффициальной документации: https://dart.dev/null-safety/understanding-null-safety

Поэтому компилятор ругается на Вас, чтобы Вы могли разрешить конфликт и объяснить ему что делать в том случае когда перемення будет равна null, чтобы снять с себя за это ответственность.

Начиная с Dart 2.0 - это ваша ответственность.

Какие могут быть варианты?

Вариант №1: мы точно знаем что там не будет null

Первый вариант - это не разрешает вышеуказанную неопределенность.
Первый вариант заключается в том, что вы пообещаете компилятору что данной неопределенности не будет.

Если мы наверняка знаем что в данном участке кода данная переменная 100% будет определена - мы можем сообщить об этом компилятору поставив !(восклицательный знак) после переменной.

var happy = isHappy! ? 'happy' : 'not happy';

таким образом мы говорим компилятору: "Не боись, под мою ответственность! Там 100 пудов не будет null. Мы его пятью строками ранее определили."
И компилятору этого достаточно, чтобы пропустить Вас дальше...ну а если приложение таки ляжет - то это будет ваша вина ¯\_(ツ)_/¯ .

Вариант №2: А пёс её знает...но если вдруг встретишь null, то считай его...

Второй вариант это разрешить данную неопределенность указав в коде какое значение необходимо подставить, если вдруг в переменной будет null.
Делается это через два ??(два вопросительных знака) после имени переменной, после которых вы указываете дефолтное значение(по умолчанию).

var happy = (isHappy ?? false) ? 'happy' : 'not happy';

В данном случае мы указываем что если переменная isHappy не будет определена - мы будем использовать false и результат этого выражения в свою очередь идет в тернарный оператор.

Также стоит отметить, что Вы используете в коде далеко не одну опциональную переменную. Вы также пытаетесь распечатать переменную hobbies.

И её также стоит обработать:

В первом случае:(точно без null)

for(var el in hobbies!) {

Во втором случае (если null, то используем пустой список):

for(var el in (hobbies ?? [])) { 

Ошибка №2

Это к слову не единственная ошибка.
Есть еще одна небольшая помарочка в конструкторе

User(name, age, [ishappy, hobbies]){
    this.name = name;
    this.age = age;
    this.isHappy = isHappy;
    this.hobbies = hobbies;    
}

Присмотритесь внимательнее:

  • в параметрах вы используете переменную ishappy (все с маленькой),
  • а в теле конструктора используете isHappy (H c большой)

Причем это довольно коварная ошибка ибо isHappy также имеется и чисто технически компилятор это пропустит. Вы просто будете сохранять в поле его же значение, а значение из параметров конструктора никогда не дойдет до назначения...

Это нужно исправить:

User(name, age, [ishappy, hobbies]){
    this.name = name;
    this.age = age;
    this.isHappy = ishappy;
    this.hobbies = hobbies;    
}

Итоговый вариант:

(я предпочту второй вариант, т.к он более надежный)
Что и Вам рекомендую

void main() {
  var bob = User('Bob', 20, true, ['footbal', 'Skate']);
  bob.info();
  
  var alex = User('Alex', 25, false, ['Basketbol']);
  alex.info();
}

class User{
  String? name;
  int? age;
  bool? isHappy;
  List<String>? hobbies;

  User(name, age, [ishappy, hobbies]){
    this.name = name;
    this.age = age;
    this.isHappy = ishappy;
    this.hobbies = hobbies;

  }

  void info(){
    var happy = (isHappy ?? false) ? 'happy' : 'not happy';
    print('User $name is $age years old. He is $happy. His hobbies:');
    for(var el in (hobbies ?? [])){
      print(el);
    }
  }
}

введите сюда описание изображения

→ Ссылка