2017年1月13日金曜日

【TAネタ】parseDoubleメソッドにはthrowが1つしかないのに、APIには2つあるのはなんで?

きっかけ

プログラミングが得意ではなかった1年生が4年後にTAをやるなんて思いもしなかった...(遠い目)

僕はTAでレポートの仮採点をしています。その時の質問をネタにしました。

parseDoubleメソッドにはthrowが1つしかないのに、APIには2つあるのはなんで?

JavaにはparseDoubleというメソッドがあります。このメソッドは、変換可能であればString型の文字列をDobule型の数値に変換することができます。

さて、APIを見てみるとこんなことが書いてあります。





Throws:
NullPointerException - if the string is null
NumberFormatException - if the string does not contain a parsable double.

どこが問題かと言うと、parseDoubleは最初に、

public static double parseDouble(String s)
                          throws NumberFormatException

と書いているのです。


NumberFormatExceptionはthrowで明示されているのに、NullPointerExceptionはどこでthrowされているのでしょう?

これを調べるにはJDKの実装を見てみるのが早いと思いました。
openJDKのDoubleを読んでみると、537行目にparseDoubleがあります。

public static double parseDouble(String s) throws NumberFormatException { 
 return FloatingDecimal.parseDouble(s);
}

どうやらFloatingDecimalに処理を投げているようです。
FloatingDecimalを読んでみると、109行目にparseDoubleがあります。

public static double parseDouble(String s) throws NumberFormatException {
 return readJavaFormatString(s).doubleValue();
}

どうやらreadJavaFormatStringに処理をなげ(ry
同じクラスの中にあるようです。1830行目にありました。

static ASCIIToBinaryConverter readJavaFormatString( String in ) throws NumberFormatException {
 boolean isNegative = false;
 boolean signSeen = false;
 int decExp;
 char c;
parseNumber: 
 try{ 
  in = in.trim(); // don't fool around with white space.
                   // throws NullPointerException if null 
  int len = in.length();
  if ( len == 0 ) {
   throw new NumberFormatException("empty String");
 }

ここで注目なのはin = in.trim()です。コメントにthrows NullPointerException if nullとあります。inはString型のフィールドです。どうやらinがnullの時、trimをするとNullPointerExceptionが投げられるようです。どういう仕組みでしょうか?(まあ、ここで調査を打ち切ってもいいのですが、納得するまで続けます。)

trimはStringクラスにあります。2867行目にあります。

public String trim() { 
 int len = value.length;

valueはchar型の配列です。lengthは長さを返します。
もし、value = nullの時、value.lengthはNullPointerExceptionを投げます。
Javaでは配列が「null」か「長さ0」かで意味が異なります。(引用: nullか長さ0の配列か)
長さ0ならNullPointerExceptionは投げないはずです。

結論

parseDoubleの内部でString型のフィールドに対してlengthを取っているため、その時に
NullPointerExceptionをthrowする。そのため、throwが2つあります、というわけでした。

0 件のコメント:

コメントを投稿