Существует множество отличных ответов, которые охватывают неприятные симптомы null, поэтому я хотел бы привести альтернативный аргумент: « Ноль» - это недостаток системы типов.
Цель системы типов состоит в том, чтобы гарантировать, что различные компоненты программы правильно сочетаются друг с другом; хорошо типизированная программа не может «сойти с рельсов» в неопределенное поведение.
Рассмотрим гипотетический диалект Java или любой другой предпочитаемый вами статически типизированный язык, где вы можете назначить строку "Hello, world!"для любой переменной любого типа:
Foo foo1 = new Foo(); // Legal
Foo foo2 = "Hello, world!"; // Also legal
Foo foo3 = "Bonjour!"; // Not legal - only "Hello, world!" is allowed
И вы можете проверить переменные так:
if (foo1 != "Hello, world!") {
bar(foo1);
} else {
baz();
}
В этом нет ничего невозможного - кто-то может создать такой язык, если захочет. Особое значение не должно быть "Hello, world!"- это , возможно, было число 42, кортеж (1, 4, 9), или, скажем, null. Но зачем ты это делаешь? Переменная типа Fooдолжна содержать только Foos - в этом весь смысл системы типов! nullне Fooбольше, чем "Hello, world!"есть. Хуже того, nullэто не значение любого типа, и вы ничего не можете с этим поделать!
Программист никогда не может быть уверен, что переменная действительно содержит a Foo, и программа не может; во избежание неопределенного поведения, он должен проверять переменные, "Hello, world!"прежде чем использовать их как Foos. Обратите внимание, что проверка строки в предыдущем фрагменте не распространяет тот факт, что foo1 действительно является Foo- barскорее всего, будет иметь свою собственную проверку, просто чтобы быть в безопасности.
Сравните это с использованием типа Maybe/ Optionс сопоставлением с шаблоном:
case maybeFoo of
| Just foo => bar(foo)
| Nothing => baz()
Внутри Just fooпредложения и вы, и программа точно знаете, что наша Maybe Fooпеременная действительно содержит Fooзначение - эта информация распространяется по цепочке вызовов и barне требует каких-либо проверок. Поскольку Maybe Fooэто тип, отличный от типа Foo, вы вынуждены обрабатывать возможность того, что он может содержать Nothing, так что вы никогда не можете быть обманутыми, если a NullPointerException. Вы можете гораздо проще рассуждать о своей программе, и компилятор может пропустить нулевые проверки, зная, что все переменные типа Fooдействительно содержат Foos. Все побеждают.