Существует множество отличных ответов, которые охватывают неприятные симптомы 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
должна содержать только Foo
s - в этом весь смысл системы типов! null
не Foo
больше, чем "Hello, world!"
есть. Хуже того, null
это не значение любого типа, и вы ничего не можете с этим поделать!
Программист никогда не может быть уверен, что переменная действительно содержит a Foo
, и программа не может; во избежание неопределенного поведения, он должен проверять переменные, "Hello, world!"
прежде чем использовать их как Foo
s. Обратите внимание, что проверка строки в предыдущем фрагменте не распространяет тот факт, что 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
действительно содержат Foo
s. Все побеждают.