Print Friendly, PDF & Email

前回は、ログイン処理の実装とセッションスコープについて学びました。

スコープも2つ目でしたからその概念には慣れてきたのではないでしょうか?

今回は、JavaBeansについて学びます。

JavaBeansとは再利用可能なJavaプログラムの部品です。

Javaというネーミングはそもそもジャワコーヒーから来ているのですが、その豆【Beans】ということで部品なのですね。

ひとまずはデータを入れる箱とお考えください。

JSPのELと相性が良いのがこのJavaBeansなのです。

ここでもまた、数あてゲームについて考えてみましょう。

2章で見たJavaBeansを使わない数あてゲームとJavaBeansを使った数あてゲームのソースを比較してみます。

< JavaBeansを使わない数あてゲーム(抜粋) >

        Kazuate kazuate = new Kazuate();     
        request.setAttribute("message", kazuate.checkTheAnswer(guess));
        request.setAttribute("answer", kazuate.getTheAnswer());
        <h1>ゲームの結果は!</h1>
        ${message}<br>
        答えは${answer}<br>
図6.1 JavaBeansを使わない数あてゲーム

< JavaBeansを使った数あてゲーム(抜粋) >

        KazuateBean kazuate = new KazuateBean();
        kazuate.checkTheAnswer(guess);
        request.setAttribute("kazuate", kazuate);
        <h1>ゲームの結果は!</h1>
        ${kazuate.message}<br>
        答えは${kazuate.answer}<br>

図6.1 のとおり、リクエストスコープに2つのオブジェクト(String型の文字列オブジェクトとInteger型の整数オブジェクト)を入れていました。

これを以下のように1つのオブジェクト(KazuateBean型のインスタンス)だけにするというのが今回のテーマです。

以下の図6.2がイメージ図になります。

< JavaBeansを使った数あてゲーム >

図6.2 JavaBeansを使った数あてゲーム
  • 図6.1と図6.2の違いは何ですか?
あなたの答え:

あまり変わらない印象を持たれた方もいるかも知れませんが、このあと、例えば、データベースで扱ったCustomerを扱うような場合に、名前、住所、誕生日やポイント数などテーブルの列数だけインスタンスをスコープに詰め込まなければいけないとしたら大変です。

顧客一人分のすべての情報を持ったCustomerBeanクラス、さらには複数のCustomerBean(今井さん、篠原さん、田渕さん、坂野さんなど)の入ったCustomerBeansクラスがあって、データベースから全員分データを取得して一度にJSPに送って表示できたら便利だと思いませんか?

ということで、 ひとまずはKazuateBean クラスからです。

package p06;

import java.io.Serializable;
import java.util.Random;

public class KazuateBean implements Serializable {

    private int answer;
    private String message;

    public KazuateBean() {
        Random random = new Random();
        this.answer = random.nextInt(10);
    }

    public void checkTheAnswer(int guess) {

        if (answer == guess) {
            this.message = "あたり";

        } else if (answer > guess) {
            this.message = "もっと大きいよ";

        } else {
            this.message = "もっと小さいよ";
        }
    }

    public int getAnswer() {
        return answer;
    }

    public void setAnswer(int answer) {
        this.answer = answer;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

今までのKazuateクラスとの変更点を見ましょう。

  • 実装しているインターフェースの観点での変更点は何ですか?
あなたの答え:
  • フィールドの観点での変更点は何ですか?
あなたの答え:
  • メソッドの観点での変更点は何ですか?
あなたの答え:

JavaBeansはデータの入れ物なので、messageの文字列をメソッドで返すのではなくフィールドに格納して持ち運ぶように変えました。

なお、JavaBeansには以下のルールがあります

  • java.io.Serializableを実装している
  • 引数のないコンストラクタを持つ
  • フィールドはprivateにした上でプロパティへのgetter/setterメソッドを持つ

以下に順を追って解説します。

1.JavaBeansはjava.io.Serializableを実装している

Serializableインターフェースを実装することでスコープ内のインスタンスを直列化してファイルに保存することができるようになります。

インスタンスを直列化してファイルに保存できれば、

  • GlassFish等のアプリケーションサーバーが停止した場合も再起動してインスタンスを復元できます
  • 一時的にメモリが溢れた場合にも退避させることができます

なお、 Serializableインターフェースはマーカーインターフェイスだということを忘れてしまった方は以前JavaSEのインターフェースのところで復習しておいてください。

2.JavaBeansは引数のないコンストラクタを持つ

今回のKazuateBeanクラスでは、たまたまKazuateクラスでも引数のないコンストラクタがありましたので特に変更点はありません。

「コンストラクタの無いJavaクラスには見えてないけれどデフォルトコンストラクタがあるはず」そう思った方はコンストラクタの事がよく分かっている方です。

その場合はデフォルトコンストラクタで結構です。

しかし、もしも引数のあるコンストラクタを作った場合はデフォルトコンストラクタは作られません。

その場合は引数のないコンストラクタを別途作る必要があります。

3.JavaBeansはフィールドはprivateにした上でプロパティへのgetter/setterメソッドを持つ

上記の 今回のKazuateBeanクラスには2つのフィールドがありました。

getter/setterメソッドとは、getXxxx、set Xxxx としたフィールドにアクセスするためのメソッドのことです。

プロパティというのはこのアクセサメソッドの Xxxxの最初を小文字にしてxxxxとしたものです

プロパティ化したいメソッド名の先頭にはget/setを付けるというルールがあります

単にフィールドの値を設定取得するだけでなく、加工することもできます。

当社の新人エンジニア研修ではXxxxのところには全てフィールド名を入れることとしますので、その限りにおいてフィールドとプロパティの間に特段の区別は必要ありません。

getter/setterメソッドはアクセサメソッドと呼ばれることもあります。

アクセサメソッドはIDEを使っていれば簡単に挿入できますので方法を講師にお尋ねください。

フィールドを private で修飾するとクラス外からはアクセスできなくなるということを我々はカプセル化と情報隠蔽のところで学びました。

そこでアクセサメソッドを使ってフィールドにアクセスします。

ここでよく勉強している人は「情報隠蔽をしてもアクセサメソッドがあれば同じではないか」と考えるかも知れません。

しかし、思い出していただきたいのですが、int型のフィールドにはint型の値であれば何でも入ったのに対して、メソッドを経由すれば例えば、マイナスの値は入らないようにすることもできます。

さらに、読み込みだけ可能で、一切書き込みができないクラスを作りたいのであれば、setterメソッドを作らなければよいのです。

なお、このプロパティの考え方は他のオブジェクト指向言語にも発展的に使われています。

例えばC#、Python、Kotlin等のプロパティはより便利に使えるようになっています。

JavaBeansを活用した数あてゲーム

では、早速Beansを使ったプログラムがどう変わるかを見てみましょう。

入力画面<index.jsp>は飛び先以外大きな変更はありません。

サーブレットはこうしてみました。

package p06;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(urlPatterns = {"/GameServlet5"})
public class GameServlet5 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        int guess = Integer.parseInt(request.getParameter("guess"));

        KazuateBean kazuate = new KazuateBean();

        kazuate.checkTheAnswer(guess);

        request.setAttribute("kazuate", kazuate);

        request.getRequestDispatcher("06Beans/result.jsp").forward(request, response);

    }
}
  • 23行目でリクエストスコープに格納しているのは何ですか?
あなたの答え:

JSPはこうしてみました。

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>数当てゲームの結果表示</title>
    </head>
    <body>
        <h1>ゲームの結果は!</h1>
        ${kazuate.message}<br>
        答えは${kazuate.answer}<br>
    </body>
</html>

このELで利用しているのがプロパティです。

プロパティを使ったELの書式

${オブジェクト名.プロパティ名}

kazuate.messageと書くことによってkazuateクラスのインスタンスのgetMessageメソッドが働くのです。

では、${kazuate.answer}という記述によって働くのはどのクラスの何というメソッドですか?

あなたの答え:

例題1

以下のJavaBeansを使って、サーブレットでインスタンスを作成し、JSPで内容を表示させなさい。

なお、66行目にあるtoStringメソッドを忘れてしまった方はJavaSEに戻って「toStringメソッドで出力内容をコントロールする」というテーマを復習してください。

package p06;

import java.io.Serializable;

public class CustomerBean implements Serializable {
    
    private int customerId;
    private String name;
    private int prefectureId;
    private String address;
    private String birthday;
    private int points;

    public CustomerBean() {
    }

    public int getCustomerId() {
        return customerId;
    }

    public void setCustomerId(int customerId) {
        this.customerId = customerId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrefectureId() {
        return prefectureId;
    }

    public void setPrefectureId(int prefectureId) {
        this.prefectureId = prefectureId;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }

    public int getPoints() {
        return points;
    }

    public void setPoints(int points) {
        this.points = points;
    }

    @Override
    public String toString() {
        return "CustomerBean{" + "customerId=" + customerId + ", name=" + name + ", prefectureId=" + prefectureId + ", address=" + address + ", bitrthday=" + birthday + ", points=" + points + '}';
    }  
}

<CustomerServlet.java>を作成してください。

下図のイメージの<show_customer.jsp>を作成してください。

出力イメージ

実験1

上記 <show_customer.jsp> に以下のようなELを書き入れた場合どのような表示になりますか?

${customer}

実験結果のメモ:

買い物かごの実装

ここまでの知識を応用して買い物かごの仕組みを作ってみましょう。

どんな知識を使うかといえば主に、セッション属性、EL、JSTLなどです。

目指したいのは下図のような仕組みです。

ポイントは赤で表示されたcartオブジェクトです。

このオブジェクトには複数のcarオブジェクトが入るとします。

カートの合計金額はどのオブジェクトが持つべきでしょうか?

そうですね、個々の車に持たせるのは変ですからcartが持つべきでしょうね。

また、このcartは車の追加や削除ができないといけません。

セッションスコープに入れることを見越してBeansとして作成します。

まずはCarクラスです。

特に特徴のないBeansですが、このあとの説明のためインスタンスを1行で作れるように引数を2つ持つコンストラクタも作成しておきました。

import java.io.Serializable;

public class CarBean implements Serializable{
	private String name;
	private int price;
        
	public CarBean() {
		super();

        }
	public CarBean(String name, int price) {
		super();
		this.name = name;
		this.price = price;
	}

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }
}

つぎにCartクラスです。

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class CartBean implements Serializable {

    private List<CarBean> list;
    private int total;

    public CartBean() {
        list = new ArrayList<>();
        total = 0;
    }

    public void setCar(CarBean aCar) {
        this.list.add(aCar);
    }

    public List<CarBean> getList() {
        return list;
    }

    public int getTotal() {
        return total;
    }

    public void setTotal(int total) {
        this.total = total;
    }
}
  • このクラスのフィールドとメソッドの役割を答えなさい。
あなたの答え:



その上で、まずはJavaSEで車2台をカートに入れて合計金額を求める処理を書いてみます。

public class CartSample {

    public static void main(String[] args) {

        CarBean c1 = new CarBean("セダン", 2590000);
        CarBean c2 = new CarBean("クーペ", 4990000);

        CartBean cart = new CartBean();

        cart.setCar(c1);
        cart.setTotal(cart.getTotal() + c1.getPrice());
        cart.setCar(c2);
        cart.setTotal(cart.getTotal() + c2.getPrice());
        
        System.out.println(cart.getTotal());

    }
}
  • 上記の処理を解説しなさい。
あなたの答え:



これをサーブレットとJSPを使って実現できればよいわけですね。

まとめ

□ JavaBeansとは再利用可能なJavaプログラムの部品であり、リクエスト/セッションスコープと相性が良い

□ JavaBeansには①java.io.Serializableを実装している②引数のないコンストラクタを持つ③フィールドはprivateにした上でプロパティへのgetter/setterメソッドを持つというルールがある

□ プロパティ化したいメソッド名の先頭にget/setを付けるというルールがある

□ プロパティはアクセサメソッドのget/setXxxxの最初を小文字にしてxxxxとしたものである。

□ ELでプロパティを扱う場合の書式:${オブジェクト名.プロパティ名}