怒りのJava学習 このページをアンテナに追加 RSSフィード

Java初心者が
Java学習の途中で感じた怒りを
叩きつけるブログ

 このブログでは
※コンパイルエラーの99%は入力ミスで出来ています
※コンパイルエラーの1%は故意の省略で出来ています

現在皆様のエラー例を募集中



↓このブログの執筆方針
はじめに

↓ブログランキングへのリンク

クリックでランキングのポイントが加算される
今のところどうでもいい

2007-11-26 なぜ反映されないのか?

[]h2DBのデータベース


 実験により、H2データベース

JDBCのURLの

jdbc:h2:test

jdbc:h2://localhost/test

を、同じデータベースだと考えるようだと分かった。


・・・

しかしNetBeans

  BnbboACDB info1 =
    new BnbboACDB("atata", "atatatata", "ata@ata.com");
  manager.persist(info1);

と書いて、実行時エラーも見られないにもかかわらず、H2データベースのBNBBOACDBテーブルにはなんの変化も無いのはどういうことなのだろうか?データベースは大文字小文字の区別が無い(テーブル名は全て大文字になる)が、Javaコードのほうが小文字を含んでいるからだろうか?と思って全て大文字にリファクタリングしたが変化なし*1

 おっかしいなぁ・・・。

*1:ところでNetBeansリファクタリング不完全なんですけど[怒]。Javaコード上でBnbboACDBを選択して名前を変える→BNBBOACDBとしたら、BNBBOACDBクラスのコードまでちゃんと変えてくれるのはいいとして、BNBBOACDBクラスのアノテーション部分を忘れてるから実行時エラー出まくりなんだけどっ[怒々]!正確にはそのクエリー部分(select以下の部分)を@NamedQuery ( name ="BnbboACDB.findByEmail", query = "SELECT b FROM BNBBOACDB b WHERE b.email = :email")のようにクラス名と一致させないと実行時エラーが出る。一々手動で変更するの面倒くせぇ!・・・テーブル名は小文字でもいいらしいな。

2007-08-06 これはこれでスパゲッティだと思うがどうか?

[][]宣言classコンストラクタの所属


 お待たせしました。不定期連載再開です。


 さて。

 まず俺の先入観を一つ披露させてもらおうか。

インスタンスの生成では

宣言クラス名コンストラクタ名

でなければならない

この思い込みが粉々に砕かれたのは忘れもしない・・・えーっと・・・・そうそう20070613の記事だった。ここで参考にしたソースコード

    Frame w = new Mouse1();

というのが有って、ビックリしたのだ。wの型名がFrameなのに対してコンストラクタの名前がMouse1(ファイル名)なのだ!違ってもいいんだナ、これ。

 まあ、Mouse1はFrameをextendsして作ったクラスなので不可能では無い→Frameのコンストラクタをオーバーライドして生成する事を許可する、という事なのだろう。

 という事は逆(Mouse1 w = new Frame();)は許されないということだろうか?

Mouse1.java:31: 互換性のない型
検出値  : java.awt.Frame
期待値  : Mouse1
    Mouse1 w = new Frame();
               ^

 やっぱりダメだった。前者は互換性があるという事はコンストラクタの方(つまりextendsされた方のコンストラクタ)には上位互換性(後発のシステム(上位)が先発のシステム(下位)からの生成物を扱えること)があるということか。まあ、当然といえば当然だな。

 しかし

※眠いので以下書きかけ状態でUPして寝る。

もういくらでも罵倒のコメントをしてくれ。

眠いのだー。つづきはあすー。

翌日追記:

さっそくnori氏のコメントが得られた。

> Frame w = new Mouse1();

これは、クラスMouse1をインスタンス化した後、Frame型にアップキャストしているということですね。なぜこんなことするのかは不明ですが…。上の文は、

Frame w = (Frame)(new Mouse1());

と同じですね。コンパイル後のクラスファイルを比較してみたところ、完全に一致しましたので間違いはないと思います。

 成る程。実験感謝する。そうかこれはキャストなのか。


ところで、6/13のソースはバグありのようです…(笑)

 うむ。

mainThread.end++; //いつかオーバーフローするが気にしない。

長期実行時には間違いなく異常が起こると思われる(フフニヤリ)。


 キャストのことは、つい先日まで僕もいまいちよく理解できてませんでした。アップキャストというのは下位クラスから上位クラスにキャストすることで、ダウンキャストというのは上位クラスから下位クラスにキャストすることなんですけど、通常、問題がないとされるのはアップキャストのほうなんですよね。ダウンキャストは問題が生じることが多いです。

 ふ〜む。この辺は俺の実験と一致するな。


ちなみに、ダウンキャストというのは、本来は「アップキャストしたものを元に戻すために使われる」そうです。

http://www.kab-studio.biz/Programing/JavaA2Z/Word/00000107.html

 たとえば、String型をObject型にアップキャストした後に、再びString型に戻すのはOKということですね。

 おお!このサイトは分かり易い!全体にちゃんと日本人が読んで分かるように書いてある。当たり前のようだが、そこんとこが重要。早速サイドバーリンク集に追加。

ダウンキャストでは、渡される参照の値や、その参照が指し示すインスタンスは全く変化しない。変わるのは、参照を格納する、参照型変数の見かけ上の型だけである。

 これは要注意だ。そしてそうでないとツジツマが合わない。extendsした方で定義した変数が読まれなくなってしまうからだ。


 しかし

「ダウンキャスト」=「アップキャストを元に戻すこと」

であるならば、「ダウン」じゃ対等の操作に見えてしまって良くない。

ここは「アンアップキャスト」とでも命名するべきだったな。

これなら”初めにアップキャストありき”と分かるだろ。

インストールする前にアンインストールできないようにな。


 ところで、そもそもこの「キャスト」の命名と仕様についても腹を立てているのだが、それはまた別のお話。



--------------------------------------------

 ところで20070618の記事で、swingの自動ダブルバッファを実現するために、それまでのextends JFrame をextends JPanel に変えたわけだが、内部で生成しているJFrameインスタンスはそのまま流用、という状況で、ちょっとおかしい印象を受けた。


 何がおかしいのかというと

    JFrame frame = new JFrame();

でフレームを生成しているのに、そのフレームにマウスイベントが装備されていることなのである。だってマウスイベントを設定しているのはextends JPanel された Graph8_2 クラスなんだぜ?じゃあ何かい?このGraph8_2 クラス内部で生成されたFrameやJFrameのフレームはすべて自動的にマウスイベントが実装されちゃうのぉ?

「所属クラスで宣言されたアダプターは問答無用に生成されたインスタンスに装備されるのか?」(これが二週間前の予告の具体的な内容)


 と、思ったがどうやら

    frame.getContentPane().add( new Graph8_2() );

で、フレームのコンテントペインに元のクラスのインスタンスを追加しているようだ。(しかしこの技、無条件に使っていいのかなぁ・・・使いようによってはバグの原因になりそうで怖い。しかも複雑に入り組んで別の意味でスパゲッティプログラムになりそう。)

 しかし相変わらずContentPaneが何なのかイマイチよく分からん。コンテナを乗せるモノだというのは良く聞くが抽象的な解説では分からん!具体的にどういうデータを扱ってるのかとかが無いぞ。APIは言うまでも無く役立たずだし。


 と言うわけで、予告の内容は半分で終わり。あとの半分は上記のように不発で終わった。


俺のマヌケぇぇぇぇぇッ!!

2007-06-27 いろんな事ができるといろんな制約をくらうのか

[]内部クラスとfinal


 前回の記事で話題となった匿名(内部)クラスとfinalの関係を知るために実験を行った。

 と言ってもやる事は無限ループを、教えてもらった匿名クラスの形式で書くこと。

 つまり、

  new Thread(new Runnable(){
    public void run(){
      while(true){
        myX = myX + myVX;
        if(myTerminusX == myX){	myVX = 0;
        }else{	myVX = (myTerminusX - myX)/50+(myTerminusX
          - myX)/Math.abs(myTerminusX - myX);
        }
        myY = myY + myVY;
        if(myTerminusY == myY){	myVY = 0;
        }else{	myVY = (myTerminusY - myY)/50+(myTerminusY
          - myY)/Math.abs(myTerminusY - myY);
        }
        try{
          Thread.sleep(20);
        }catch(InterruptedException ee){
        }
          frame.repaint();
        }
      }
    }).start();

 とした。

すると案の定

MovaBall2.java:65: ローカル変数 frame は内部クラスから
アクセスされます。final で宣言される必要があります。
      frame.repaint();
      ^

 というコンパイルエラーが出たが、このエラーメッセージはあいまいである(対処法の主語が不明)。「内部クラスからアクセスされます」がそもそも変な日本語なのであるがそれはおく。

frameは内部クラスがアクセスするので

  i ) frameをfinal宣言しなければならない

  ii) 内部クラスをfinal宣言しなければならない

 のどちらかだが、匿名クラスを宣言する部分にfinalをつけてもダメだった。

つまり、

new final Thread

new final Runnable()

public final void run()

果ては

final frame.repaint();(むちゃくちゃだ(笑))

としても、たいがい

<identifier> がありません。

というエラーが出る。(final付け足しただけで有るものを無いと言い張るこのエラーもおかしいが、いい加減javacのアホさ加減に慣れてきたのかもう怒る気にもなれない。)


 まあ、そもそもnew演算子とfinalを併記する文法が許されてなさそうなので無謀な試みなのだが、ものは試しだ、ということでやってみたが、あえなく玉砕。

 んで、JFrame型のframeを宣言する頭にfinalをつけたらうまくいった。(コンパイルエラー無し→正常動作。)


 ・・・という所で気が付いた。

 nowakay氏の

あ、「JFrame frame = new JFrame()」の行の頭に「final」つける必要がありました。

 の意味はこれだったのだ。内部クラス内で呼び出されるインスタンスはfinalである必要があるらしい。なぜかは分らないが。

 nowakay氏の説明は

匿名(内部)クラスのローカル変数は矛盾を避けるためfinalを明示する必要がある

と言うものだったが、そもそも今回のループ内部では匿名クラス独自のローカル変数は用いておらず、問題になったのはmainメソッドで宣言しているインスタンスである。

 ところで、親クラスフィールドで宣言したstaticな変数myX、myVX等についてはコンパイラは全くお咎め無しであるのが腑に落ちない。

 そこでframeをクラスフィールドで宣言してみた。mainメソッドで使うのでもちろんstaticにする。

 するとコンパイルエラーなし。動作も問題ない。


 これはどういうことなのか?

 まさかstatic宣言とfinal宣言が同一の働きをするわけでもあるまい。

 うまくいく方法は分ったが、この両方が成り立ってしまう原因がさっぱり理解できない。どうなっとるんじゃい!


ぐおぉぉぉ!分らんっ!!

何じゃこのJava


 ・・・待てよ?


 と思い、ググって見たらJavaFAQに内部クラス類の説明が丁寧になされていて、その辺のカラクリも解き明かされていた。

[S016 A-14]

ローカルな内部クラスのインスタンスのライフタイム(生成されてからGCされるまでの期間)は、

それを宣言するブロックやメソッドのライフタイムより長いことがあり、

その場合、内部クラスから参照しているローカル変数や引数が実際に参照を行なう

タイミングまで存在し続けていないことがあります。

そういったケースでも正しく値が参照できることを保証するため

参照可能なものを内部クラスが生成される時点以降に値が変更されないもの、

すなわち、final宣言されているローカル変数や引数のみに制限し、

その値をコピーしてインスタンス内部に保持するようになっています。

 なるほど。

 だから同様に、staticなフィールド変数も消えないからこれも内部クラス内では使える、と言うことなんだな。(ということはバーチャルJavaマシン内部ではクラス変数(このブログで言うところのジェネラル変数)は”常に存在するもの”とされていると考えられる。)

 ただ、コピーを保持しているインスタンスが匿名クラスインスタンスなのか元クラスインスタンスなのか不明だが、よく考えたらどっちでもプログラミングには関係なかったので考えないことにした。

2007-06-19 今日もそれほど障害もなく・・・

[]クリック先に移動

 クリックした地点にボールが移動する・クリックした瞬間に方向を変えられる、ように改造した。

 もともとこの動作をさせるために配列に位置情報を入れるプログラムをこれまで考えていたのだが、いざ作ってみると配列など不要だった。

import java.awt.event.* ;
import javax.swing.* ;
import java.awt.Graphics ;
import java.awt.Color ;

public class MovaBall2 extends JPanel
                    implements MouseListener {
  static int myX = 300;
  static int myY = 150;
  static int myTerminusX = 200;
  static int myTerminusY = 150;
  static int myVX = 0;
  static int myVY = 0;
  static int r = 20;                    // 円の半径
  final static Color bc = Color.green;  // 背景色
  public MovaBall2() {
    addMouseListener(this);
    setBackground(bc);
  }//this
  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.setColor(new Color(255,255,255));
    g.fillOval(myX-r,myY-r,r*2,r*2);
  }
  public void mouseClicked(MouseEvent e) { }
  public void mouseEntered(MouseEvent e) { }
  public void mouseExited(MouseEvent e) { }
  public void mousePressed(MouseEvent e) {
    myTerminusX = e.getX();
    myTerminusY = e.getY();
  }
  public void mouseReleased(MouseEvent e) { }
  public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.addWindowListener(new WindowAdapter() { 
      public void windowClosing(WindowEvent e) { System.exit(0); }
    });
    frame.getContentPane().add( new MovaBall2() );
    frame.setSize(400,300);
    frame.setVisible(true);
    while(true){
      myX = myX + myVX;
      if(myTerminusX == myX){
        myVX = 0;
      }else{
        myVX = (myTerminusX - myX)/50 
              +(myTerminusX - myX)/Math.abs(myTerminusX - myX);
      }
      myY = myY + myVY;
      if(myTerminusY == myY){
        myVY = 0;
      }else{
        myVY = (myTerminusY - myY)/50
              +(myTerminusY - myY)/Math.abs(myTerminusY - myY);
      }
      try{
        Thread.sleep(20);
      }catch(InterruptedException ee){
      }
      frame.repaint();
    }
  }
}

f:id:java1rou:20070619181533g:image

 うまく動作してくれた。

 目的地にきちんと着地・停止させるのが意外に難しい。おかげで座標計算が非常に見苦しい形になったし、挙動も少々おかしい。その元凶は終点近くでの速度が1以下になってしまうのを避けるためヘンな式にしているからで、別に1式で実現しなくても場合分けで速度を定義すればいいのだろうが、面倒なので(式をいじくり回すほうが面倒だった気がするが)、不恰好だがとりあえずこれで良しとする。

 技術とはこういう所に現れるのだろう。


 うまくいかない原因は自分の腕なので、今日も怒らずに終わる。

2007-06-18 珍しくすんなり行ってキモチイイ

[]自動ダブルバッファリング


 paintComponent( )でググるとこんなページがトップでHIT。

16.2 Swing コンポーネントの描画

(慶応義塾大学情報処理教育室の講座のサイト18年度版より)

【例2】

マウスでクリックした場所に円を書いてみましょう。

【図省略】

ソース・プログラム Graph8.java

 これは!以前ベースにしたサンプルに良く似た動作じゃないか!それをswingで書いてあるのか!

 昨日の紹介文のように、paintComponent( )の親メソッド(super)を呼び出してるし、これは本物のswing技術のようだ。

import java.awt.event.* ;
import javax.swing.* ;
import java.awt.Graphics ;
import java.awt.Color ;

public class Graph8 extends JPanel implements MouseListener {
  int x = -1;                           // 中心のx座標
  int y = -1;                           // 中心のy座標
  int r = 30;                           // 円の半径
  final static Color bc = Color.white;  // 背景色
  final static Color dc = Color.green;  // 描画色

  public Graph8() {
    addMouseListener(this);
    setBackground(bc);
  }

  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    if(x>=0) {
      g.setColor(dc);
      g.drawOval(x-r, y-r, 2*r, 2*r);
    }
  }

  public void mouseClicked(MouseEvent e) { }
  public void mouseEntered(MouseEvent e) { }
  public void mouseExited(MouseEvent e) { }
  public void mousePressed(MouseEvent e) {
    x = e.getX();
    y = e.getY();
    repaint();
  }

  public void mouseReleased(MouseEvent e) { }
  public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.addWindowListener(new WindowAdapter() { 
      public void windowClosing(WindowEvent e) { System.exit(0); }
    });
    frame.getContentPane().add( new Graph8() );
    frame.setSize(200, 200);
    frame.setVisible(true);
  }
}

※勝手に転載しています。


 しかし、ウインドウを閉じるときの終了動作を記述する部分がAWTのままなのが気になる。

 swingにもちゃんと

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

(「本格Java入門:佐々木整:技術評論社」に載っていた。)

という簡単かつswing純正のメソッド(JFrame所属)があるのになんでAWT系列のややこしいwindowClosing書式(class java.awt.event.WindowAdapter のメソッド)を使うのか?

 まあ、やってる事は全く同じだからいいけど。


 ともかく、ベースが似ているので改造は楽に違いない、と思い早速同じ動作をさせることにする。


 ここで、以前小耳に挟んだ「swingは自動でダブルバッファを実現している」という機能をついでに試してみることにする。つまりAWTではシコシコ書いていたダブルバッファリングの操作をばっさり削除してグラフィクスコンテキストに色々書き込んでからrepaint()するだけ、としてみる。APIに載っていた「グラフィクスコンテキストのコピーを取って変更しろ」はひとまず無視する

import java.awt.event.* ;
import javax.swing.* ;
import java.awt.Graphics ;
import java.awt.Color ;

public class Graph8_2 extends JPanel implements MouseListener {
  final static int lim = 256*2;//256だと追い越され得るから
  static int[] bX = new int[lim]; //位置情報格納配列
  static int[] bY = new int[lim]; //位置情報格納配列
  static int[] bColor = new int[lim]; //色 情報格納配列
  static int sta = 0; //配列最前列位置(後に描画)
  static int end = 0; //配列最後尾位置(先に描画)
  int r = 30;                           // 円の半径
  final static Color bc = new Color(237,237,237);  // 背景色
  final static Color dc = Color.green;  // 描画色

  public Graph8_2() {
    bX[0]     = -100;
    bY[0]     = -100;
    addMouseListener(this);
    setBackground(bc);
  }//this

  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    if(bX[end]>=0) {
      if(sta != end && bColor[sta] == 237){bColor[sta] = 238;sta++;}
      if(sta >= lim ){sta = 0;}
        int i = sta;			
        while(true){
          if(bColor[i] < 237){
          bColor[i]++;
          g.setColor(new Color(bColor[i],bColor[i],bColor[i]));
          g.fillOval(bX[i]-r,bY[i]-r,r*2,r*2);
        }
        if(i == end){break;}
        i++;
        if(i >= lim){i = 0;}
      }//for
    }
  }

  public void mouseClicked(MouseEvent e) { }
  public void mouseEntered(MouseEvent e) { }
  ublic void mouseExited(MouseEvent e) { }
  public void mousePressed(MouseEvent e) {
    end++;
    if(end>=lim) {end = 0;}
    bX[end]     = e.getX();
    bY[end]     = e.getY();
    bColor[end] = 0;
  }
  public void mouseReleased(MouseEvent e) { }
  public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.addWindowListener(new WindowAdapter() { 
      public void windowClosing(WindowEvent e) { System.exit(0); }
    });
    frame.getContentPane().add( new Graph8_2() );
    frame.setSize(400, 300);
    frame.setVisible(true);
    while(true){
      try{
        Thread.sleep(100);
      }catch(InterruptedException ee){
      }
      frame.repaint();
    }
  }
}

f:id:java1rou:20070618122106g:image

 うまくいった。super.paintComponent(g)やgetContentPane()のように戸惑う命令もあるが、前のAWTでのダブルバッファよりも簡単なコードで・たまのちらつきもなく・クリックへの応答性もすこぶる良く、言うことなしである。これは使わない手は無い。

 今回は気持ちよく行ったので怒らずに終わる。


 しかし正直、swing以後でこれを教えない解説書は不勉強のそしりを免れないであろうと確かに思う。