Hay muchas respuestas excelentes que cubren los síntomas desafortunados de null
, por lo que me gustaría presentar un argumento alternativo: Null es una falla en el sistema de tipos.
El propósito de un sistema de tipos es asegurar que los diferentes componentes de un programa "encajen" adecuadamente; un programa bien escrito no puede "descarrilarse" en un comportamiento indefinido.
Considere un dialecto hipotético de Java, o cualquiera que sea su lenguaje de tipo estático preferido, donde puede asignar la cadena "Hello, world!"
a cualquier variable de cualquier tipo:
Foo foo1 = new Foo(); // Legal
Foo foo2 = "Hello, world!"; // Also legal
Foo foo3 = "Bonjour!"; // Not legal - only "Hello, world!" is allowed
Y puede verificar variables de esta manera:
if (foo1 != "Hello, world!") {
bar(foo1);
} else {
baz();
}
No hay nada imposible en esto: alguien podría diseñar ese lenguaje si quisiera. El valor especial no tiene que ser "Hello, world!"
- que podría haber sido el número 42, la tupla (1, 4, 9)
, o, por ejemplo, null
. ¿Pero por qué harías esto? Una variable de tipo Foo
solo debe contener Foo
s, ¡ese es el punto principal del sistema de tipos! null
No es Foo
más de lo que "Hello, world!"
es. Peor aún, null
no es un valor de ningún tipo, ¡y no hay nada que puedas hacer con él!
El programador nunca puede estar seguro de que una variable realmente contenga un Foo
, y tampoco puede hacerlo el programa; Para evitar un comportamiento indefinido, debe verificar las variables "Hello, world!"
antes de usarlas como Foo
s. Tenga en cuenta que hacer la verificación de cadena en el fragmento anterior no propaga el hecho de que foo1 es realmente un Foo
- bar
probablemente también tendrá su propia verificación, solo para estar seguro.
Compare eso con el uso de un Maybe
/ Option
tipo con coincidencia de patrones:
case maybeFoo of
| Just foo => bar(foo)
| Nothing => baz()
Dentro de la Just foo
cláusula, tanto usted como el programa saben con certeza que nuestra Maybe Foo
variable realmente contiene un Foo
valor: que la información se propaga por la cadena de llamadas y bar
no necesita hacer ninguna verificación. Debido a que Maybe Foo
es un tipo distinto Foo
, se ve obligado a manejar la posibilidad de que pueda contener Nothing
, por lo que nunca puede ser sorprendido por a NullPointerException
. Puede razonar sobre su programa mucho más fácilmente y el compilador puede omitir comprobaciones nulas sabiendo que todas las variables de tipo Foo
realmente contienen Foo
s. Todos ganan.