※本記事は、“Curly Braces #5: Null is not nothing” の翻訳記事です。

2022年6月17日| 6分読む

Eric J. Bruno


nullの意味およびnullとvoidの違いは何ですか

 

nullとは何ですか。簡単な答えはありません。このエッセイが Seinfeld のエピソードだったら、それは文字通り何もない列であるため、「Null Column」と呼ばれていたかもしれません。しかし、よく考えてみると、nullは無ではありません。そのためにvoid があるのです。実際、 void はnullよりもはるかに明確な概念です。 void は純粋です。void は単純です。 void は忠実で揺るぎないものです。しかし、void はnullではありません。もし null が Facebook にあったら、その関係ステータスは「it’s complicated」になるでしょう(図1を参照)。

Image stating it’s complicated
図 1. Null の概念をFacebookの関係ステータスで表わすと「complicated」です

私が強く推薦する素晴らしい本「ゼロ: 危険なアイデアの伝記」の中で、Charles Seifeは、ゼロを古代の数字系に欠けていた、かつて邪悪だった概念と説明しています。ゼロまたは何もないという考え(私にはnullのようなもの)は、著者によれば、人間の思想の最も偉大なパラドックスの1つです。その発明と受け入れは、分数や計算など、他の大きな発見につながりました。

UNIXでは、nullはデバイスにすることができます。 /dev/null のように、書込み時にブラック・ホールのように動作し、何も出力されなくなります。これは、ヌルモデムケーブルとは対照的で、すべてのデータは確実に出力されるが、電気的に交差するだけです。

一般的な言葉では、契約書の条項や、間違った日や間違った人に使用される割引特典など、あるものは同時に「nullおよびvoid」になる場合があります。ただし、その概念はプログラミング言語では決して当てはまりません。nullとvoidの両方になることありません。

私の好きな映画、トロン、の中で悪の組織サークとその手下の一人が登場するシーンで、サークは怒りと失望のあまり、誤ったプログラムを「Null Unit」と呼んでいます。これは、サークが空虚さ、知性の欠如、失望、または単なるフラストレーションを意味しています。

振り返ってみると、TI-BASIC、Commodore BASIC、そしてAmigaの68000 Assemblyの若年時代にプログラミングを始めました(当時はCコンパイラを購入できませんでした)。いずれもnullの概念はありませんでした。しかし、nullは考え方であり、概念であり、抽象化であるため、精神的に存在していました。それは愛に似ています: 正確に定義することはできませんが、手にした時にわかるのです。愛のように、nullは、プログラムがnullポインタ(つまり null 参照)でクラッシュしたときのような沈んだ気持ちです。

 

nullは多言語

プログラミング言語の場合、nullは言語によって異なることを意味し、使用方法を制御するルールのセットが異なります。たとえば、Cではnullはゼロです。Brian KernighanとDennis RitchieのThe C Programming Languageでも、いくつかのコード例ではnull (well、 NULL)のかわりに0を使用します。

char* alloc(int n) {
    if ( allocbuf + ALLOCSIZE - allocp >= n ) {
        allocp += n;
        return allocp - n;
    } else /* no room */
        return 0;
}

マクロでは、nullは単にゼロとして定義されます。

#define NULL 0

Cでは、nullはfalseとも混同されます。次のコードはコンパイルされます。

bool b = false;
int y = 0;
if ( !b && !y ) {
    return 1;
}
return 0;

上のスニペットは、次の内容を出力します。

RUN FINISHED; exit value 1

nullポインタ定数である可能性があります。このCコードでもコンパイルされます。

int main(int argc, char** argv) {
    int* p = 0;
    int  q = NULL;
    return q;
}

はい、上記のコードでは警告が出るかもしれませんが、実行すると、値はゼロのままプログラムが終了します。

RUN FINISHED; exit value 0;

とても混乱するものです。

JavaScriptでは、nullはnullです。実際、NULL、NullまたはNilと呼ぶことはできますが、undefined とは呼ばないでください。=== (3つの等号)ではなく== (2つの等号)でチェックしないかぎり、これはまったく異なるものです。この文脈では、単にnullを ”nullっぽい” と呼ぶことにしましょう。

PythonはNoneを使用してnullを表すため、有望に見えます。しかし、Noneの振る舞いを見ていると、Noneもまた、あなたを失望させるものであることに気づくでしょう。たとえば、関数returnをNoneに設定すると、予期したとおりの処理が実行されます。何も返されません。ただし、Noneを返す関数の値を出力すると、次のようになります。

>>> eric=None
>>> print(eric)
None

Pythonでは、Noneという値を出力すると、Noneという単語をスペルアウトするテキストの形式で何かが表示されます。これは天才的な皮肉です。

 

Javaはどうでしょうか?

確かにJavaはnullは正しく扱われますよね?Javaでは、nullはnull参照です。ただし、nullがnullポインタの場合を除きます。

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Object.toString()" because "ref" is null
     com.ericbruno.nullreference.Test.main(Test.java:11)

とはいえ、Java では nullには意味があります。それは voidではありません。メソッドが戻り値 void で定義されている場合、何も返すことはできません。ただし、メソッドが Objectを返すように宣言されている場合、voidは返せませんが、必ずnullを返すことができます(退屈する場合は、実際の Objectを返すこともできます)。

また、最近Bruce Eckelから、Java switch文でnullが正当な case なる可能性があることを学習しました。

より正確には、nullはJavaキーワードではなく、Javaのリテラルです。

Java言語仕様によると、リテラルはプリミティブ型、 String 型またはnull型の表現です。

仕様で述べられているように、null型には1つの値があります。null参照はリテラルnullで表されます。一方、null型は名前がありません。すでにわかっているように、nullリテラルは、プリミティブ型ではなく参照型にのみ割り当てることができます。null参照をJavaの任意の参照型にキャストできますが、 instanceof からの戻り値は常にfalseで、Javaではnullはnullであることを意味します。

public class Test {
    public static void main(String[] args) {
        Integer i = (Integer)null;
        if ( i instanceof Integer ) {
            System.out.println("i is of type Integer");
        }
    }
}

上記のコードの結果は何も出力されません。Pythonよろしく!

 

結論: すべてのポインタ

このnull探索のポイントは何の意味があるのでしょうか?たとえ nullの概念があやふやであったとしても、nullは現代のプログラミングに欠かせない要素なのです。nullを使用せずに、Javaでコードを書かされたり、C++、JavaScript、Pythonなどでも同様のことをさせられたりすることを想像してみて下さい。nullの代入もなく。nullのチェックはもなく、nullは何もない。nullなしのコーディングは、適切な例外処理を行えば可能だと思いますが、その場合でも、JDKやその他のAPIコールで、オプションでnullをパラメータとして受け取る場合、これは課題となるでしょう。

 

より詳しい情報