トップ «前の日記(2005-05-12) 最新 次の日記(2005-05-14)» 編集

日々の破片

Subscribe with livedoor Reader
著作一覧

2005-05-13

_ こないだはまったおばかなバグ

public String foo(String[] array) {
  StringBuffer sb = new StringBuffer();
  for (int i = 0; i < array.length; i++) {
    sb.append(array[0]);
  }
  return new String(sb);
}
....
assertEquals("ABCD", o.foo(new String[] { "A", "B", "C", "D" }));

と抽出すると、これで気づかなきゃおかしいだろう。AAAAとなるわけだから。

実際は、

public String foo(String[] classNames) {
  for (int i = 0; i < classNames.length; i++) {
    try {
        Class.forName(classNames[0]);
    } catch (Exception e) {
      IllegalArgumentException ia = new IllegalArgumentException("bad class name:" + classNames[i]);
      ia.initCause(e);
      throw ia; 
    }
  }
}
....
try {
  o.foo(new String[] { "jp.co.foo.BAAAA", "jp.co.foo.Bar" });
  fail("no exception");
} catch (Exception e) {
  assertTrue(e.printStackTrace(), e instanceof IllegalArgumentException); // 追記(これ第1引数はそのままは書けないよ)
}
try {
  o.foo(new String[] { "jp.co.foo.Bar", "jp.co.foo.BAAAAA" });
  fail("no exception");
} catch (Exception e) {
  assertTrue(e.printStackTrace(), e instanceof IllegalArgumentException);
}

で、何度やっても2番目の呼び出しでno exceptionとなる。素直に自分を疑えばよいのに(というか疑っているのだが、例外処理のほうに気を取られてclassNames[0]としていることにまったく気づかず。

考察1:APIがたとえば、foo(List classNames)だったらどうだろうか?

for (Iterator i = classNames.iterator(); i.hasNext();) {
  Class.forName((String)i.next());

とするから、少なくてもここでストールするこたないだろう。(実際には長い文字列をsplitした結果だから配列を利用するほうが手数が少ないので配列を利用しているので、それなりの必然性はある)

考察2:ソースコードレベルデバッガを使う場合は面倒がらずにかっちりと使う。(forNameの次の文とcatchの内側に仕掛けていたので与えている文字列がclassNames[0]だとは思いもしなかった)

考察3:こういうときはprintfデバッグでも良いかも。

  for (int i = 0; i < classNames.length; i++) {
    try {
        System.out.println("start check:" + classNames[i]);
        Class.forName(classNames[0]);  // で気づかないとますますはまるだけだけど。
    } catch (Exception e) {

_ J2SE 1.5のfor-each

あらためて見てみたら、この例はおもしろいな。

for (Iterator i = suits.iterator(); i.hasNext(); )
    for (Iterator j = ranks.iterator(); j.hasNext(); )
        sortedDeck.add(new Card(i.next(), j.next()));
これはバグ。でも、
for (Suit suit : suits)
    for (Rank rank : ranks)
        sortedDeck.add(new Card(suit, rank));
}
こっちはOK。でも、なんかな……単純なIterator#nextの置換と考えて逆に痛い目に合いそうな気もするのだが。
本日のツッコミ(全4件) [ツッコミを入れる]
_ leajist (2005-05-13 15:09)

はじめまして。<br>J2SE5.0であれば配列でも拡張for文でまわせるので、考察1と同じ形にできそうですね。<br>for (String className : classNames) {<br>  Class.forName(className);<br>}<br><br>また5.0以前でも java.util.Arrays.asList() を使用すれば配列からListの変換は1手できるので、foo(List classNames) にしても手数という点ではあまり変わらないかもしれません。

_ arton (2005-05-13 15:28)

なるほど。1.5の場合のfor文拡張は基本的に利用すべきでしょうね。

_ junpei (2005-05-13 21:13)

for文の拡張はgenericsと一緒にリリースされたために真価を発揮してますよね

_ unibon (2005-05-15 10:23)

1.5未満の枠組み内でも、ループ変数 i をループ内で使っていないことで、コンパイラーに変数未使用の警告を出してほしいです。


2003|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|

ジェズイットを見習え