NULLと""(ダブルクォーテーション2つ)の違いについてのふとした疑問です。
String型の変数をクリアするときは(1)と(2)のどちらがよいでしょうか
(1)変数名 = null;
(2)変数名 = "";
他人のコードを読むと(1)が多く、(2)は初心者の方に多いように思えます。
どちらでも意味的にはよいのですが、それぞれ選んだ理由をおしえてください。
プログラミングの作法的な観点からの回答などであればうれしいですが、どんなことでもいいです。
ユーザは「""」を入力するかも知れません。
ユーザが「null」を入力することはないでしょう。
従って、初期化というなら「null」のほうが良いと思います。
パフォーマンスとかメモリをケチるとかその辺の理由もあるでしょう。
空文字かどうかのチェックしたければ、大抵は両方を見ることになるわけですか。
String型変数にnullを指定するということは、その変数を明示的に「値が設定されていない」状態にすることになります。一方、""を指定するのは「空白な文字列を割り当てる」事を意味します。
この、「値が割り当てられているか否か」ということが、使い方に関わってきます。分かりやすい例では、nullを割り当てた変数に対して参照を行うと、Null Pointer Exceptionが発生することになります。
String st1r = null;
String str2 = "Value:"+str1; ←Exceptionになる
では空白文字列を設定した方がいいかとなると、そういうわけでもありません。変数に値が割り当てられているか否かで処理を分岐するような場合です。
String str = null;
<<何かの処理>>
if( str!=null ) {
<<何かの処理で値が割り当てられた場合>>
} else {
<<そうでない場合>>
}
処理によっては、空白文字列も意味を持つこともありますので、上記のような判定で空白文字列か否かを基準に使うのは正しくないとなります。
まぁ要はケースバイケースということですね。
。。。すみません
public static void main(String args[]){
String str1 = null;
String str2 = "Value:"+str1;
}
↑Exceptionは起きませんでした。。。でも意味はわかりますよ。
こちらも、
>処理によっては、空白文字列も意味を持つことも
とうことで、条件分岐の判定で混同してしまう問題があるということですね?ありがとうございます。
hissssaさんとほぼ同じ意見です。
Null Pointer Exception例外をcatchして動くようなプログラミングをしたいのならnullを使うのもありかと思います。
try{
何かの処理
}catch( NullPointerException e){
初めて値が入るとみなして設定させる処理
}
など。
例外ハンドラーを使う書き方はうまく使うとif文のネストや条件の列挙を減らして全体の見通しをよくすることができます。
しかし空文字列の判定にnullを使うというのはプログラムの意味的にはおかしいですね。たぶんあとで混乱する元になりそうな気がします。
つまり、(1)のようにNullで初期化するとNullPointerExceptionを利用した書き方もできる、ということですね。ありがとうございます。
では、変数が空であるという判定にnullを使うと、なぜおかしいのでしょうか?
Stringをクラスとして認識しているか、プリミティブ型と認識しているかの違いと思われます。
JavaではStringクラスは他のクラスとは違い、文字列の連結を+で行うことができますし、
JavaのルートクラスであるObjectクラスにはtoStringメソッドが存在します。
私の場合、使い捨てのいわゆる「動けばいいや」的なプログラムを作る場合は""でクリアしてしまい、
できるだけ忌々しいNullPointerExceptionを出さないようにします。
そして、仕事で作る場合は、バグの兆候を示すNullPointerExceptionを
可能な限り検出する確率を上げたいのでnullでクリアします。
きちんとしたものを作るときは、Nullのクリアの方がいいということですかね。参考にさせていただきます。ありがとうございます。
自分なら、
・初期化をしたいなら null
・初期値を設定したいなら "" (空文字列)
を使うと思います。
null は明示的に値が存在しない事を表すので本来的には意味が近いはず。
Nullで初期化するとNullPointerExceptionを利用した書き方もできる
可能では有りますが、大抵の場合推奨されません。
理由はいくつか有りますが、
・NullPointerException が RuntimeException であるために文法上 catch を強制されず、取りこぼす可能性がある。
・NullPointerException に限らず、Exception の発生は現在の Java の実装においてコストが高い。(負荷が高い)
ため、正常系の実装としては避けるべきだと思います。
もちろん、本来の意味の Exception の発生は問題ありません。(アルゴリズムのミスなどで null でないはずの場所で null だったなど)
では、変数が空であるという判定にnullを使うと、なぜおかしいのでしょうか?
変数が「空」である判定なら問題ありません。
id:Kumappus さんは「空文字列」の比較に使うのはおかしいと仰っています。
良いか悪いかは置くとして、変数が「空(null)」と「空文字列」とはプログラム上別の意味を持っているということです。
う~ん、だんだん難しくなってきました。つまり逆にいうと、
・初期化には""は使わない。
・初期値を設定するときにはNullは使わない。
ということですか?
今度は「初期化」と「初期値を設定する」ことの違いがわからなくなってしまいました・・・。
たとえば宣言が、
String 変数名 = new String();
のような変数の場合は、初期値が無いのでどちらかというと「クリア」=「初期化」だと思うので、Nullのほうですかね。。。?
>・NullPointerException が RuntimeException であるために文法上 catch を強制されず、取りこぼす可能性がある。
catchしなくてもコンパイルできるという意味ですよね?この場合、NullPointerExceptionを想定しているはずですが、ついうっかり、ということがあると面倒ですよね。なるほど。
>・NullPointerException に限らず、Exception の発生は現在の Java の実装においてコストが高い。(負荷が高い)
あまり使わないほうがいいかもしれませんね。。
>id:Kumappus さんは「空文字列」の比較に使うのはおかしいと仰っています。
確かに、「空文字列の判定に・・・」と仰ってますね。意味を間違えたようです。
では、変数が空であるという判定にnullを使うと、なぜおかしいのでしょうか?
「空文字列」と「null」が別物だから。
どう別物であるのか、普段どう意識されているのか教えていただけるとありがたいです。
変数が初期化されている/されていないということと、変数の値が何も指してないことを表している(null)/長さ0の文字列を表している("")は、まったく異なるものです。どう別物なのか?と聞かれても困るのですが、別物として扱うことができるようにjavaはそういう仕様になっているわけです。
java ではローカル変数で String s; と書いたとき、sは初期化されていません。初期化されてない変数の値を参照するとコンパイルエラーになります。クラス変数、インスタンス変数はnullで初期化される仕様です。
String s = null;と書けばsはnullで初期化されます。
String s = "";と書けばsは""で初期化されます。
sがnullかどうか調べたいときは if (s==null)と書きますし、
sが""かどうか調べたいときは if (s.equals(""))と書きます。
そして("" == null)や("".equals(null))は必ずfalseになります。
「クリアする」というのはあなたが作成するプログラムの仕様としてあなたが決めることです。nullを代入するのをクリアすると決めてもよいし、""を代入するのをクリアすると決めても良いのです。
それはあなたが解決したい問題の種類によります。常に何かを指した状態を維持したいのであれば""を使えばよいし、そうでないならnullを使うほうが簡単です。
null(何も指してない)と空(長さ0の文字列)の違いを意識したいのであれば、C言語か、アセンブリ言語レベルの低レベルなプログラミングを学習すればよいと思います。
ありがとうございます。
(1)、(2)のどちらを選ぶかはそのプログラムの仕様で決まり、それは作る人が決めればいいということですね。
では実際、皆さんはどちらの仕様で作っているのでしょう?またどういった理由で決めているのでしょう?それほどレアなケースではないと思うのですが、どうでしょう。
ケースバイケースだと思いますよ。
初期化すら失敗している、あるいは不正に値がNullになっていることエラーとして検地したいのであればNullPointerExceptionを発生させるためにNullを使いますし、画面表示に初期値をそのまま使うのでNullであれば(Exceptionが発生して)困る場合は""を使います。
ありがとうございます。
とりあえずnull派です。
何も無い順に書くとこんな感じですが・・・
"" だとクリアせずに変数に文字列を代入した気分になります。
String含めてどんな変数にしろ、色々変化する値はプログラム中で与えたいので、null でクリアしたくなります。
変数名 = ""は、クリアでなく文字列の代入という気分。となると、
何らかの値が入った変数に""を代入する→""が入ったので前の値は消された→クリアした
という考えは、あまりしないということでしょうか?
ありがとうございます。
追記。
オブジェクトを設計した際に、クリアした際にとる値(初期値とか)は定義するものなんで、その値に従えばいいです。
それが、空白文字列なら””でいいし、”名前を入力してください”というコメントかもしれない。
でも、特に戻すべき値が明示されず、プログラムの都合上クリアしたいだけならば、nullの方が気分的に良いというわけです。
クリアした際にとる値を定義したことは、数えるほどしかないです。。。
あまりしないものかと思ってました。
参考にいたします。ありがとうございます。
nullか""(空文字)かという議論は他の人にお任せしまして、私なりの意見を言えば、「String型の変数を初期化」する必要はほとんどありません。
初期化という意味が、(ローカル)変数などを使用した後にメモリを開放するようなイメージで使われているならば不要といえます。
>|java|
public void doSomething()
{
String str = "abc";
// 処理
str = null; // or "";
}
||<
このような意味でstrに再度nullを代入したところで、無駄な処理が増えるだけです。
どちらにせよこの変数strはローカルでしか使用されないため、メソッドを抜けてしまえば"abc"というインスタンスはGCの対象となります。
(厳密には文字列は特別なプールにキャッシュされるのでGCの対象となるかはJVMの実装によるかもしれない)
よってメモリの節約にはなりません。
また、
>|java|
public void doSomething()
{
String str = null;
// (中略)
str = "abc";
// 処理
str = "efg";
}
||<
というように最初に変数を宣言し、そのときに初期化するのは空文字かnullかという話であるならば、原則として変数の宣言は使用するときにその都度行う方が好ましい為に次のようなコードとなります。
>|java|
public void doSomething()
{
// (中略)
String abc = "abc";
// 処理
String efg = "efg";
}
||<
これに関してはコーディング規約に関連する話題となります。
ただし、try-catch節の外でどうしても変数の宣言が必要という場合はnullで初期化しておく事が好ましくなります。(JDBCでConnectionの処理を行う場合が代表的ですね)
理由としては、""(空文字)は意味のある文字列であり、このケースでは「(初期化が必要なケースで)まだ初期化していない」からです。
また、Beanなどのインスタンス変数の初期値としてのStringに関しては、好みとしか言いようがありません。
>|java|
public class Book
{
private String title = null;
}
||<
ただし、省略した場合にnullがデフォルト値としてインスタンス変数に設定されることや、空文字に意味がある場合もあることを考慮するとnullのが好ましいのでは?と私は考えます。
以上、他の方とは異なる見方ですが、参考になれば幸いです。
参考になります。ありがとうございます。
なるほど。
初期化されてるのか入力値なのかの区別ということですね。ありがとうございます。