目次


Java プログラミング入門、第 1 回

Java 言語の基本

Java プラットフォーム上でのオブジェクト指向プログラミング

Comments

コンテンツシリーズ

このコンテンツは全2シリーズのパート#です: Java プログラミング入門、第 1 回

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:Java プログラミング入門、第 1 回

このシリーズの続きに乞うご期待。

はじめに、このチュートリアルに期待すべきこと、そしてチュートリアルを最大限に活用する方法を確認してください。

このチュートリアルについて

この全 2 回からなるチュートリアル「Java プログラミング入門」は、Java テクノロジーに馴染みのないソフトウェア開発者を対象としています。第 1 回と第 2 回の両方に取り組むことで、オブジェクト指向プログラミング (OOP) を理解し、Java 言語およびプラットフォームを使用して実際のアプリケーション開発に取り組めるようになります。

この第 1 回では、Java 言語を使用した OOP について段階的に学びます。まず、Java プラットフォームと Java 言語の概要を紹介し、続いて Java Development Kit (JDK) と Eclipse IDE からなる開発環境をセットアップする手順を説明します。そして開発環境の構成要素について紹介した後、基本的な Java 構文の実践学習を開始します。

第 2 回では、より高度な言語機能として、正規表現、ジェネリック、入出力、シリアライズなどを取り上げます。また、第 1 回で開発し始める Person オブジェクトに基づいて、プログラミングの例を紹介します。

目標

第 1 回を修了すると、Java 言語の基本的な構文を理解し、単純な Java プログラムを作成できるようになります。この基本知識を基に、「Java プログラミング入門、第 2 回: 実際のアプリケーションに対応するための構成体」で Java プログラミングの学習を続けてください。

前提条件

このチュートリアルは、Java コードや Java プラットフォームをまだ扱ったことのないソフトウェア開発者を対象としています。OOP の概念については、このチュートリアルで概要を説明します。

システム要件

このチュートリアルの演習を行うには、以下のソフトウェアをインストールして、開発環境をセットアップする必要があります。

  • Oracle の JDK 8
  • Eclipse IDE for Java Developers

上記ソフトウェアのダウンロードおよびインストール手順は、このチュートリアルで説明します。

推奨されるシステム構成は以下のとおりです。

  • Java SE 8 をサポートする、メモリー容量 2GB 以上のシステム。Java 8 は、Linux、Windows、Solaris、および Mac OS X 上でサポートされています。
  • ソフトウェア・コンポーネントとサンプル・コードをインストールするための 200MB 以上のディスク・スペース。

Java プラットフォームの概要

Java テクノロジーは、消費者向けデバイスから異機種混合のエンタープライズ・システムに至るまで、多岐にわたるアプリケーションの開発に使用されています。このセクションでは、Java プラットフォームとその構成要素の概要を説明します。

Java 言語

あらゆるプログラミング言語と同じように、Java 言語には独自の構造、構文規則、プログラミング・パラダイムがあります。Java 言語のプログラミング・パラダイムは、この言語の機能がサポートする OOP の概念に基づいています。

Java は C 言語から派生した言語なので、構文規則は C 言語とよく似ています。例えば、コード・ブロックはメソッドにモジュール化されていて、各メソッドは中括弧 ({}) で区切ります。また、変数は最初に宣言してから使用します。

構造としては、Java 言語は「パッケージ」から始まります。パッケージは、Java 言語の名前空間メカニズムです。パッケージにはクラスが収容され、クラスにはメソッド、変数、定数などが収容されます。このチュートリアルでは、Java 言語のこれらの構成要素について学びます。

Java コンパイラー

Java プラットフォーム用のプログラムを作成するときは、.java ファイル内にソース・コードを作成した後、それらのファイルをコンパイルします。コンパイラーは Java 言語の構文規則に照らし合わせてコードをチェックした上で、「バイトコード」を .class ファイルに書き出します。バイトコードとは、Java 仮想マシン (JVM) 上で動作するように意図された一連の命令のことです。JVM レベルで抽象化するという点で、Java コンパイラーは他の言語のコンパイラーとは異なります。他の言語のコンパイラーは、プログラムが実行される CPU チップセットに適した命令を書き出します。

JVM

JVM は、実行時に .class ファイルを読み取って解釈し、そのプログラムの命令をネイティブ・ハードウェア・プラットフォーム (JVM を作成する際にターゲットとされたハードウェア・プラットフォーム) 上で実行します。JVM がバイトコードを解釈する方法は、CPU がアセンブリー言語の命令を解釈する方法と同様ですが、JVM は特定のプラットフォーム専用に作成されるソフトウェアであるという点で CPU とは異なります。Java 言語の「一度書けば、どこでも実行できる」という原則は、JVM を中心としたものです。適切な JVM 実装を利用できるチップセットであれば、どのチップセット上でも Java コードを実行できます。JVM は Linux や Windows などのメジャーなプラットフォームで使用でき、携帯電話や愛好家向けチップ用の JVM にはJava 言語のサブセットが実装されています。

ガーベッジ・コレクター

Java プラットフォームには最初からメモリー管理機能が備わっているため、開発者自身が (あるいはサード・パーティー製ライブラリーを使用して) メモリー割り当てに対処する必要はありません。実行時に Java アプリケーションがオブジェクト・インスタンスを作成すると、JVM はそのオブジェクトに対し、「ヒープ」から自動的にメモリー領域を割り当てます (ヒープとは、プログラムが使用するメモリーとは別に確保されるメモリーのプールのことです)。メモリー領域が割り当てられたオブジェクトは、バックグラウンドで実行される Java の「ガーベッジ・コレクター」によって追跡されます。アプリケーションでオブジェクトが不要になると、ガーベッジ・コレクターがそのオブジェクトからメモリー領域を回収します。開発者がメモリーを処理するためのコードを作成する必要はないことから、このメモリー処理手法は「暗黙的メモリー管理」と呼ばれています。ガーベッジ・コレクションは、Java プラットフォームのパフォーマンスに不可欠の機能の 1 つです。

Java Development Kit

Java Development Kit (JDK) をダウンロードすると、(コンパイラーとその他のツールと併せて) ビルド済みユーティリティーを包括的に揃えたクラス・ライブラリーを入手できます。これらのユーティリティーを利用すれば、一般的なアプリケーション開発タスクのほとんどを達成することができます。JDK パッケージとライブラリーの幅の広さを知る最善の方法として、このリンク先の JDK API ドキュメンテーションをチェックしてください。

Java Runtime Environment

Java ランタイムとも呼ばれる Java Runtime Environment (JRE) には、Java 言語で作成されたプログラムを実行するために必要な JVM、コード・ライブラリー、コンポーネントが含まれています。JRE はさまざまなプラットフォームで使用できます。JRE ライセンスの条件に従っていれば、作成したソフトウェアと一緒にそれを実行するために使用するプラットフォームをアプリケーション・ユーザーに提供する目的で、アプリケーションと併せて JRE を再配布することもできます。JRE は、JDK に含まれています。

Java 開発環境をセットアップする

このセクションでは、JDK と最新リリースの Eclipse IDE をダウンロードしてインストールし、Eclipse 開発環境をセットアップします。

JDK と Eclipse IDE がすでにインストールされている場合は、「Eclipse を使い始める」のセクション、またはその次の「オブジェクト指向プログラミングの概念と原則」のセクションにスキップして構いません。

使用する開発環境

JDK には JRE の完全なコピーをはじめ、Java コードをコンパイルして実行するために使用できる一連のコマンドライン・ツールが含まれています。これらのツールだけを使用してアプリケーションを開発することもできますが、ほとんどの開発者は IDE の追加機能、タスク管理、ビジュアル・インターフェースを高く評価しています。

Java 開発でよく使われているオープンソースの IDE としては、Eclipse が挙げられます。Eclipse はコードのコンパイルやデバッグなどといった基本的タスクを処理してくれるので、開発者はコードの作成とテストに集中できます。さらに Eclipse を使用すれば、ソース・コード・ファイルをプロジェクト別に整理して、プロジェクトごとにコンパイルとテストを行うこともできます。その上、プロジェクト・ファイルを保管するソース・リポジトリーに数の制限はありません。Java 開発で Eclipse を使用するには、JDK がインストールされている必要があります。Eclipse バンドルのいずれかをダウンロードすると、そのバンドルに JDK が付属しています。

JDK をインストールする

以下の手順に従って、JDK をダウンロードしてインストールしてください。

  1. このリンク先のページ「Java SE ダウンロード」にアクセスし、「Java Platform (JDK) (Java プラットフォーム (JDK))」ボックスをクリックして最新バージョンの JDK のダウンロード・ページを表示します。
  2. ダウンロードするバージョンの「Accept License Agreement (ライセンス契約に同意する)」を選択してライセンス契約に同意します。
  3. 使用しているオペレーティング・システムとチップ・アーキテクチャーに対応するダウンロードを選択します。

Windows

  1. 保存先を選択するよう促されたら、ファイルをハード・ディスクに保存します。
  2. ダウンロードが完了したら、インストール・プログラムを実行します。JDK をハード・ディスクの覚えやすい場所 (例: C:\home\Java\jdk1.8.0_92) にインストールします (この例のように、選択したインストール・ディレクトリーの名前に更新番号を含めることをお勧めします)。

OS X

  1. ダウンロードが完了したら、そのダウンロード・ファイルをダブルクリックしてマウントします。
  2. インストール・プログラムを実行します。JDK のインストール先は選択できません。Mac 上で JDK 8 がインストールされた場所を確認するには、/usr/libexec/java_home -1.8 を実行します。これにより、/Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home のようなパスが表示されます。

Solaris や Linux にインストールする場合の手順を含め、詳細についてはこのリンク先の JDK 8 と JRE 8 のインストール・ページを参照してください。

コンピューター上の Java 環境が整ったので、次は Eclipse IDE をインストールします。

Eclipse をインストールする

Eclipse をダウンロードしてインストールするには、以下の手順に従います。

  1. このリンク先の Eclipse パッケージのダウンロード・ページにアクセスします。
  2. 「Eclipse IDE for Java EE Developers」をクリックします。
  3. 右側の「Download Links (ダウンロード・リンク)」の下で、使用しているプラットフォームを選択します (使用している OS タイプがサイトによってすでに判別されている場合もあります)。
  4. ダウンロード元として選ぶミラーをクリックします。その後、ダウンロード・ファイルをハード・ディスクに保存します。
  5. ダウンロードが完了したら、ファイルを開き、デフォルト値を受け入れてインストール・プログラムを実行します。

Eclipse をセットアップする

Eclipse IDE は抽象化によって JDK の詳細を隠すという利便性を提供しますが、それでも JDK と JDK のさまざまなツールにアクセスしなければならないことに変わりはありません。Eclipse を使用して Java コードを作成するには、その前に、JDK が配置されている場所を Eclipse に指示する必要があります。

以下の手順に従って、Eclipse 開発環境をセットアップしてください。

  1. ローカル・ハード・ディスクから Eclipse を起動します (私の例の場合、/Users/sperry/eclipse/java-neon から起動します)。
  2. どのワークスペースを開くかを尋ねられたら、デフォルトを選択します。
  3. 「Welcome to Eclipse (Eclipse へようこそ)」ウィンドウを閉じます (このようこそウィンドウは、新しいワークスペースを開くたびに表示されますが、この動作を無効にすることもできます。それには、「Always show Welcome at start up (起動時に常にようこそウィンドウを表示)」チェック・ボックスからチェック・マークを外します)。
  4. 「Preferences (設定)」 > 「Java」 > 「Installed JREs (インストール済み JRE)」の順に選択します。Eclipse の JRE セットアップ・ウィンドウを示す図 1 に、この選択項目が強調表示されています。
    図 1. Eclipse で使用する JDK を構成する
    Eclipse 内での正しい JDK JRE のセットアップを示す画面のスクリーンショット
    Eclipse 内での正しい JDK JRE のセットアップを示す画面のスクリーンショット
  5. Eclipse が指している JRE が、JDK と一緒にダウンロードしたものであることを確認します。インストールされている JDK を Eclipse が自動的に検出しない場合は、「Add... (追加...)」をクリックし、表示されるダイアログ・ボックス内で「Standard VM (標準 VM)」をクリックしてから、「Next (次へ)」をクリックします。
  6. JDK のホーム・ディレクトリー (例えば、Windows 上では C:\home\jdk1.8.0_92) を指定してから、「Finish (完了)」をクリックします。
  7. 目的の JDK が選択されていることを確認したら、「OK」をクリックします。

Eclipse のセットアップが完了しました。これで、プロジェクトを作成し、Java コードをコンパイルして実行できるようになりました。次のセクションでは、Eclipse について詳しく説明します。

Eclipse を使い始める

Eclipse は単なる IDE ではなく、完全な開発エコシステムです。このセクションでは、Java 開発で Eclipse を使用する方法を具体的な手順に沿って紹介します。

Eclipse 開発環境

Eclipse 開発環境は、以下の 4 つのメイン・コンポーネントからなります。

  • ワークスペース
  • プロジェクト
  • パースペクティブ
  • ビュー

Eclipse 内での基本的な編成単位はワークスペースです。作成するプロジェクトのすべては、ワークスペースに格納されます。パースペクティブは、それぞれのプロジェクトの全体像を捉える手段であり (したがって、この名前が付いています)、パースペクティブ内には 1 つ以上のビューがあります。

図 2 に示す Java パースペクティブは、Eclipse のデフォルト・パースペクティブです。Eclipse を起動すると、このパースペクティブが表示されます。

図 2. Eclipse の Java パースペクティブ
Eclipse IDE 起動時の画面に表示されるデフォルトの Java パースペクティブのスクリーンショット
Eclipse IDE 起動時の画面に表示されるデフォルトの Java パースペクティブのスクリーンショット

Java パースペクティブ内には、Java アプリケーションの作成を開始するために必要なツールがあります。図 2 に示されているタブ付きウィンドウのそれぞれが、Java パースペクティブのビューとなります。このうち特に有用なビューは、「Package Explorer (パッケージ・エクスプローラー)」と「Outline (アウトライン)」の 2 つです。

Eclipse 環境は細かく構成できるようになっています。例えば、各ビューはドッカブル・ビューとなっているので、Java パースペクティブ内の任意の位置に移動したり、使いやすい場所に固定したりできます。けれども今のところは、デフォルトのパースペクティブとビューのセットアップを使用して作業を続けます。

プロジェクトを作成する

以下の手順に従って、新しい Java プロジェクトを作成してください。

  1. 「File (ファイル)」 > 「New (新規)」 > 「Java Project... (Java プロジェクト...)」の順にクリックして「New Java Project (新規 Java プロジェクト)」ウィザードを開始します (図 3 を参照)。
    図 3. 「New Java Project (新規 Java プロジェクト)」ウィザード
    「New Java Project (新規 Java プロジェクト)」ウィザードのスクリーンショット
    「New Java Project (新規 Java プロジェクト)」ウィザードのスクリーンショット
  2. プロジェクト名として Tutorial と入力します。プロジェクトのワークスペースとしては、Eclipse を起動したときに開いたワークスペースの場所を使用します。
  3. 使用している JDK を確認します。
  4. 「Finish (完了)」をクリックしてプロジェクトのセットアップを受け入れ、プロジェクトを作成します。

新しい Eclipse Java プロジェクトとソース・フォルダーを作成したので、この開発環境を使用して実際の開発に取り掛かることができます。けれども開発を進めるには、OOP パラダイムを理解していなければなりません。そこで、次のセクションでは OOP について説明します。

オブジェクト指向プログラミングの概念と原則

Java 言語は (大部分が) オブジェクト指向の言語です。このセクションでは、構造化プログラミングと対比させてオブジェクト指向プログラミング (OOP) 言語の概念を紹介します。

オブジェクトとは何か

オブジェクト指向の言語は、C や COBOL などの構造化プログラミング言語とは異なるプログラミング・パターンに従っています。構造化プログラミング・パラダイムは極めてデータ指向であり、データ構造を確立した上で、そのデータに対してプログラム命令を実行します。Java 言語のようなオブジェクト指向の言語では、データとプログラム命令を「オブジェクト」に結合します。

オブジェクトは自己完結型のエンティティーであり、属性と動作だけが含まれます。構造化プログラミング言語では、フィールド (属性) を含めてデータ構造を作成し、その構造を実行対象 (動作) としてプログラム・ロジックのあらゆる部分に渡します。それとは異なり、オブジェクト指向の言語ではデータとプログラム・ロジックが結合されます。データとプログラム・ロジックが結合される粒度はさまざまです。Number という非常に細かい粒度のオブジェクトでデータとプログラム・ロジックが結合されることもあれば、大規模なバンキング・アプリケーションでは、FundsTransfer サービスといった粒度の粗いオブジェクトで結合されることもあります。

親オブジェクトと子オブジェクト

「親オブジェクト」は構造的基礎となるオブジェクトであり、親オブジェクトを複雑化して派生するのが「子オブジェクト」です。子オブジェクトは親オブジェクトと見掛けは似ていますが、より特化されています。オブジェクト指向のパラダイムでは、子オブジェクトに親オブジェクトの共通の属性と動作を再利用し、さらに子オブジェクトごとに異なる属性と動作を追加することができます。

オブジェクト間のやりとりと調整

オブジェクトが互いにやりとりするには、メッセージ (Java 用語では「メソッド呼び出し」) を送信します。さらに、オブジェクト指向のアプリケーション内では、プログラム・コードでオブジェクト間のアクティビティーを調整して、特定のアプリケーション・ドメインのコンテキスト内でタスクを実行します。

オブジェクトの要約

上手く作成されたオブジェクトの特性は以下のとおりです。

  • 境界が明確に定義されている
  • アクティビティー・セットの有限集合を実行する
  • 自身のデータと、自身のアクティビティーを達成するために必要な他のオブジェクトについての情報だけを持っている

要するに、オブジェクトは個別のエンティティーであり、自身のタスクを実行するために最小限必要なオブジェクトにだけ依存します。

次は、Java オブジェクトが実際にどのようなものなのかを見ていきます。

例: Person オブジェクト

この最初の例では、一般的なアプリケーション開発シナリオに基づき、個人を Person オブジェクトで表します。

オブジェクトの定義についての説明でおわかりのように、オブジェクトには属性と動作という 2 つの主要な要素があります。これから、それらの要素を Person オブジェクトに適用する方法を説明します。

大まかに言うと、オブジェクトの属性は名詞に相当し、動作は動詞に相当すると思ってください。

属性 (名詞)

個人にはどのような属性があるでしょうか?以下に、一般的な属性をいくつかリストアップします。

  • 名前
  • 年齢
  • 身長
  • 体重
  • 目の色
  • 性別

他にも思いつく属性はたくさんあるはずですが (あとでいくらでも属性を追加できます)、出発点としては、上記の属性で十分です。

動作 (動詞)

実際の個人は、あらゆる類の行動をとることができます。けれどもオブジェクトの動作は、一般に何かしらのアプリケーション・コンテキストに関連します。例えばビジネス・アプリケーションのコンテキストで、Person オブジェクトに「あなたの体格指数 (BMI) は?」と尋ねるとします。それに対する応答として、Person はその身長と体重の属性を使用して BMI を計算します。

これよりも複雑なロジックを Person オブジェクト内部に隠すこともできますが、とりあえず、Person は以下のように動作するとします。

  • BMI を計算する
  • すべての属性を出力する

状態とストリング

OOP での重要な概念は、「状態」です。いかなる時点でも、オブジェクトの状態はオブジェクトの属性値によって表されます。

Person の場合、その状態は名前、年齢、身長、体重などの属性によって定義されます。これらの属性をまとめて提示するとしたら、String クラスを使用してリストを作成することになります (これについては、あとで詳しく説明します)。

状態とストリングの概念を併せて使用することで、Person に「属性のリスト (つまり、String) を提示して、あなたのことを教えてください」と頼むことができます。

OOP の原則

これまで構造化プログラミングの経歴を積んできたとしたら、OOP の価値提案はまだ漠然としていることでしょう。結局のところ、個人の属性とこれらの属性の値を取得 (および変換) するためのロジックは、C や COBOL でも作成できます。けれども OOP を決定づける原則を理解すると、OOP パラダイムの利点が明らかになってきます。その原則とは、カプセル化、継承、ポリモーフィズムです。

カプセル化

何にもまして、オブジェクトは個別であること、つまり自己完結型であることを思い出してください。この特性が、「カプセル化」を有効にする原則です。自己完結型であり、外部から保護されているというオブジェクトの特性を表現するために、「隠蔽 (hiding)」という用語が使われることもあります。

どの用語を使うかに関わらず、重要なのは、オブジェクトがその状態と動作を外部の世界から隔離する境界を維持するという点です。実際の世界でのオブジェクトのように、コンピューター・プログラミングで使用されるオブジェクトには、同じアプリケーション内で使用される他のカテゴリーのオブジェクトに対するさまざまなタイプの関係があります。

Java プラットフォーム上では、「アクセス修飾子」(あとで説明します) を使用して、オブジェクト間の関係性を「public」から「private」に変更することができます。public アクセスが指定されたオブジェクトにはどこからでも制限なく利用できる一方、private アクセスが指定されたオブジェクトの属性には、そのオブジェクト内部でしかアクセスできません。

この public/private の境界によって、オブジェクト指向の原則であるカプセル化が適用されます。Java プラットフォーム上では、オブジェクトごとにこの境界の強度を変えることができます。カプセル化は Java 言語の強力な機能の 1 つです。

継承

構造化プログラミングで新しいエンティティーを作成する通常の手法は、構造をコピーして新しい名前を指定し、そこに属性を追加したり、既存の属性を変更したりして、コピー元の構造とは異なるエンティティー (Account レコードなど) にするというものです。この手法では、やがて、重複するコードが大量に生成されて、保守の問題が生じる可能性があります。

OOP は、新しいクラスの作成に「継承」という概念を導入し、特化する新しいクラスに (コードを追加することなく) ソース・クラスの属性と動作を「コピー」できるようになっています。コピーした属性または動作の中に変更しなければならないものがある場合は、それをオーバーライドします。特化したクラスを作成するために必要なのは、変更対象のソース・コードだけです。ソース・オブジェクトは「親」と呼ばれ、特化された新しいオブジェクトは「子」と呼ばれます (これらの用語については、すでに紹介しました)。

例えば、人材アプリケーションを作成する際に、Employee という名前の新しいクラスの基底クラス (「スーパークラス」とも呼ばれます) として、Person クラスを使用するとします。Person の子である Employee には、Person クラスのすべての属性に加えて、以下のクラスを追加します。

  • 納税者番号
  • 従業員番号
  • 給与

継承を利用すれば、Person のコード全体を手作業でコピーすることなく、新しい Employee クラスを簡単に作成できます。

ポリモーフィズム

カプセル化や継承に比べ、「ポリモーフィズム」は理解するのが難しい概念です。ポリモーフィズムとは基本的に、階層の同じブランチに属する複数のオブジェクトに同じメッセージを送信して (つまり、同じ動作を指示して)、オブジェクトによって異なる動作をさせることを意味します。

ポリモーフィズムがビジネス・アプリケーションのコンテキストにどのように適用されるかを理解するために、Person の例を再び取り上げます。前述のように、Person に対し、その属性を String にフォーマット化するよう指示するとします。Person クラスで、その特定の Person のタイプに応じて属性の表現をさまざまに変えられるようにするのがポリモーフィズムです。

ポリモーフィズムは Java プラットフォーム上で OOP に取り組む際に遭遇する最も複雑な概念の 1 つなので、この入門編チュートリアルの範囲外とします。カプセル化と継承については、以降のセクションで詳しく説明します。

Java は純粋なオブジェクト指向の言語ではありません

Java 言語を純粋なオブジェクト指向の言語 (Smalltalk など) から差別化する特性は 2 つあります。その 1 つは、Java 言語にはオブジェクトと プリミティブ型 が混在していることです。もう 1 つは、あるオブジェクトの内部構造を、そのオブジェクトを使用する他のオブジェクトに公開するコードを作成できることです。

Java 言語は、正しい OOP の原則に従って正しいオブジェクト指向のコードを作成するために必要なツールを提供することは確かです。けれども Java は純粋なオブジェクト指向の言語ではないため、コードの作成方法について自制する必要があります。Java 言語は OOP の規則を強要しないので、自分で規則を守るしかないのです。「優れた Java コードを作成する方法」のセクションで、そのためのヒントを紹介します。

Java 言語を使い始める

1 つのチュートリアルで Java 言語の構文全部を紹介するのは不可能なので、第 1 回の残りの部分では Java 言語の基本に焦点を絞り、皆さんに単純なプログラムを作成できるようになるだけの知識と実践力を身に付けてもらいたいと思います。OOP の中心となるのはオブジェクトです。そこで、このセクションではまず、Java 言語でのオブジェクトの処理方法に具体的に関連する 2 つのトピックとして、予約語と Java オブジェクトの構造について説明します。

予約語

あらゆるプログラミング言語と同じく、Java 言語でもコンパイラーが特殊な単語として認識する特定の単語が指定されています。これらの単語を Java 構成体の名前に使用することはできません。以下のように予約語 (「キーワード」とも呼ばれます) の数は驚くほど少数です。

abstract
assert
boolean
break
byte
case
catch
char
class
const
continue
default
do
double
else
enum
extends
final
finally
float
for
goto
if
implements
import
instanceof
int
interface
long
native
new
package
private
protected
public
return
short
static
strictfp
super
switch
synchronized
this
throw
throws
transient
try
void
volatile
while

これとは別に Java 構成体の名前に使用できない単語 (厳密には、キーワードというよりも リテラル ) として、truefalsenull があります。

IDE を使用してプログラミングを行う利点は、構文の色分け機能によって予約語を確認できることです。

Java クラスの構造

クラスは、属性と動作を格納する個別のエンティティー (オブジェクト) の青写真です。クラスによってオブジェクトの基本構造が定義されます。アプリケーションは実行時に、この定義されたオブジェクトのインスタンスを作成します。オブジェクトの境界と状態は明確に定義されていて、処理する内容を正しく指示すれば、その通りの処理を行います。あらゆるオブジェクト指向の言語には、クラスの定義方法に関する規則があります。

Java 言語の場合、リスト 1 に示すようにクラスを定義します。

リスト 1. クラスの定義
package packageName;
import ClassNameToImport; 
accessSpecifier class ClassName {
  accessSpecifier dataType variableName [= initialValue];
  accessSpecifier ClassName([argumentList]) {
    constructorStatement(s)
  }
  accessSpecifier returnType methodName ([argumentList]) {
    methodStatement(s)
  }
  // This is a comment
  /* This is a comment too */
  /* This is a
     multiline
     comment */
}

リスト 1 には、さまざまなタイプの構成体が含まれています。そのうち、行 1 の package、行 2 の import、行 3 の class予約語のリストに含まれているため、リスト 1 でのこの 3 つは予約語として扱わなければなりません。リスト 1 の他の構成体には、それぞれの構成体が表現する概念を説明する名前を指定しています。

リスト 1 の行 11 から行 15 はコメント行です。ほとんどのプログラミング言語では、プログラマーがコメントを追加してコードを文書化できるようになっています。Java の構文では、単一行のコメントと複数行のコメントを使用できます。

// This is a comment
/* This is a comment too */
/* This is a
multiline
comment */

単一行のコメントは 1 行に納めなければなりませんが、単一行のコメントを連続させてコメントのブロックという形にすることもできます。複数行のコメントは /* で始まり */ で終わる限り、何行にわたっていても構いません。

次は、リスト 1 の構成体のそれぞれを詳しく説明します。まずは、package の説明から始めます。

クラスのパッケージ化

Java 言語では、作成するクラスの名前を選択できます (AccountPersonLizardMan など)。けれども時には、わずかに異なる概念を表すために同じ名前が使われてしまうこともあります。「名前衝突」と呼ばれるこの状況は、頻繁に発生します。このような競合を解決するために、Java 言語では「パッケージ」を使用します。

Java パッケージは、「名前空間」を提供するためのメカニズムです。名前空間とは、その外部では一意ではないとしても、その内部ではすべての名前が一意である領域を指します。構成体を一意に識別するためには、構成体の名前空間をその名前の一部にして完全に修飾する必要があります。

パッケージは、個別の機能単位を使用して複雑なアプリケーションを作成する手段としても利用できます。

パッケージを定義するには、package キーワードの後に正当なパッケージ名を続けてセミコロンで終了します。パッケージ名は通常、以下の事実上の標準スキームに従います。

package  orgType.orgName.appName.compName;

上記のパッケージ定義は、以下の構成要素からなります。

  • orgType は、組織のタイプです (comorgnet など)。
  • orgName は、組織のドメイン名です (makotojavaoracleibm など)。
  • appName は、アプリケーションの短縮名です。
  • compName は、コンポーネントの名前です。

このチュートリアルでは一貫して上記の表記規則を使用します。パッケージ内の Java クラスは、常にこの表記規則に従って定義することをお勧めします (Java 言語では、このパッケージ規則に従うことは要件となっていません。パッケージを指定すること自体、要件となっていませんが、パッケージを指定しない場合には、すべてのクラスの名前が一意となる状態でデフォルト・パッケージに含まれるようにしてください)。

import 文

クラス定義内で次に使用されているのは (リスト 1 を参照)、「import 文」です。import 文は Java コンパイラーに対し、コード内で参照されているクラスが配置されている場所を指示します。自明ではないクラスは何からの機能のために他のクラスを利用するのが常ですが、import 文は、それらのクラスを Java コンパイラーに指示するための手段になります。

通常、import 文は以下のような形になります。

import ClassNameToImport;

import キーワードの後に、インポートするクラスを続けてセミコロンで終了します。クラス名は「完全修飾名」でなければなりません。つまり、名前には、そのクラスを格納するパッケージを含める必要があります。

パッケージ内のすべてのクラスをインポートするには、パッケージ名の後に .* を続けます。例えば、以下の文は com.makotojava パッケージの含まれるすべてのクラスをインポートします。

import com.makotojava.*;

ただし、パッケージ全体をインポートするとコードが読みにくくなることがあるので、完全修飾名を使って必要なクラスだけをインポートすることをお勧めします。

クラス宣言

Java 言語でオブジェクトを定義するには、クラスを宣言する必要があります。クラスは、クッキーの抜き型のような、オブジェクトのテンプレートだと考えてください。

リスト 1 に、このクラスの宣言が記載されています。

accessSpecifier class ClassName {
  accessSpecifier dataType variableName [= initialValue];
    accessSpecifier ClassName([argumentList]) {
    constructorStatement(s)
  }
  accessSpecifier returnType methodName([argumentList]) {
    methodStatement(s)
  }
}

クラスの accessSpecifier に設定できる値は 1 つだけではありませんが、通常は値 public です。accessSpecifier の他の値については、この後すぐに説明します。

クラスの名前はほとんど任意に指定できますが、慣例では「キャメル・ケース」を使用することになっています。つまり、連結された各単語の先頭文字を大文字にして、残りの文字はすべて小文字にするという形です。クラス名には文字と数字だけを使用できます。これらのガイドラインに従っている限り、同じ表記に従っている他の開発者にとって使用しやすいコードになります。

変数とメソッド

クラスには、「変数」と「メソッド」という 2 つのタイプの「メンバー」を持たせることができます。

変数

クラスのメンバーである変数の値により、そのクラスの各インスタンスが区別され、そのインスタンスの状態が定義されます。これらの値は通常、「インスタンス変数」と呼ばれます。変数には以下の属性があります。

  • accessSpecifier
  • dataType
  • variableName
  • (必要に応じて) initialValue

accessSpecifier には以下の値を使用できます。

  • public: この値を設定した変数は、あらゆるパッケージに含まれるすべてのオブジェクトに可視になります (この値は決して使用しないでください。補足記事「パブリック変数」を参照)。
  • protected: この値を設定した変数は、同じパッケージ内に定義されているオブジェクト、または (任意のパッケージ内に定義されている) サブクラスに可視になります。
  • アクセス修飾子なし (「フレンドリー・アクセス」または「パッケージ private アクセス」とも呼ばれます): この場合の変数は、同じパッケージ内に定義されているクラスに属するオブジェクトにだけ可視になります。
  • private: この値を設定した変数は、その変数が使用されているクラスにだけ可視になります。

変数の dataType は、それが何の変数であるかによって決まります。例えば、プリミティブ型であったり、別のクラス型であったりする場合もあります (あとで詳しく説明します)。

variableName には開発者が自由に値を設定できますが、慣例では、変数の名前にはキャメル・ケースの表記を使用し、先頭文字だけを例外として小文字にします (このスタイルは、「ローワー・キャメル・ケース」と呼ばれることもあります)。

initialValue については、とりあえず気にする必要はありません。クラスを宣言するときに、インスタンス変数を初期化できるということを知っていれば十分です (初期化しない場合、クラスをインスタンス化する際に、コンパイラーによって生成されたデフォルトが設定されます)。

例: Person の場合のクラス定義

皆さんがこれまでに学んだ内容の要約として、リスト 2 に Person のクラス定義の例を記載します。

リスト 2. Person の基本的なクラス定義
package com.makotojava.intro;

public class Person {
   private String name;
   private int age;
   private int height;
   private int weight;
   private String eyeColor;
   private String gender;
}

上記の Person の基本的なクラス定義は、この時点では役に立ちません。それは、Person の属性 (private の属性) を定義しているに過ぎないためです。より完全なものにするためには、Person クラスに動作が必要です。つまり、「メソッド」を追加する必要があります。

メソッド

クラスのメソッドによって、そのクラスの動作が定義されます。

メソッドを大きく分けると、「コンストラクター」であるメソッドと、コンストラクター以外のすべてのメソッドという 2 つカテゴリーに分類されます。コンストラクター以外のメソッドには、さまざまなタイプがあります。コンストラクター・メソッドは、クラスのインスタンスを作成するためにだけ使用されます。コンストラクター以外のメソッドは、事実上、あらゆるアプリケーション動作に使用できます。

リスト 1 のクラス定義をもう一度見ると、メソッドの構造を定義するために、以下のような要素が使用されていることがわかります。

  • accessSpecifier
  • returnType
  • methodName
  • argumentList

メソッドの定義に含まれるこれらの構造要素の組み合わせは、メソッドの「シグニチャー」と呼ばれます。

ここからは、2 つのメソッド・カテゴリーについて詳しく調べます。まずは、コンストラクターから見ていきましょう。

コンストラクター・メソッド

コンストラクターは、クラスをインスタンス化する方法を指定するために使用します。リスト 1 に、コンストラクターを宣言する構文が抽象的な形で示されています。その部分を以下に抜粋します。

accessSpecifier ClassName([argumentList]) {
  constructorStatement(s)
}

コンストラクターの accessSpecifier は、変数で使用するものと同じです。コンストラクターの名前は、クラスの名前と一致していなければなりません。したがって、クラスの名前が Person であれば、コンストラクターの名前も Person にする必要があります。

デフォルト・コンストラクター以外のコンストラクターを使用する場合 (補足記事「コンストラクターを使用するかしないかは任意です」を参照)、そのコンストラクターに以下のいずれか、または両方を含めた argumentList を渡します。

argumentType argumentName

argumentList には、各引数をコンマで区切って含めます。同じ名前の 2 つの引数を含めることはできません。また、argumentType は、プリミティブ型あるいは別のクラス型のいずれかになります (変数の型の場合と同じです)。

コンストラクターを使用する場合のクラス定義

ここで、Person オブジェクトを作成する機能を追加するための 2 つの方法を説明します。引数なしのコンストラクターを使用するという方法と、属性の部分的リストを初期化するという方法です。

リスト 3 に、コンストラクターの作成方法を示します。ここには、argumentList を使用する方法も示されています。

リスト 3. コンストラクターを使用した Person のクラス定義
package com.makotojava.intro;
public class Person {
  private String name;
  private int age;
  private int height;
  private int  weight;
  private String eyeColor;

  private String gender;
  public Person() {
    // Nothing to do...
  }

  public Person(String name, int age, int height, int weight String eyeColor, String gender) {
    this.name = name;
    this.age = age;
    this.height = height;
    this.weight = weight;
    this.eyeColor = eyeColor;
    this.gender = gender;
  }
}

リスト 3 では、変数を代入するために this キーワードを使用していることに注意してください。this キーワードは、Java での「this object」の省略表現です。同じ名前を持つ 2 つの変数を参照するときは、このキーワードを使用しなければなりません。この例の場合、コンストラクターが取るパラメーターでもクラス変数でも age を使用しているため、this キーワードを使用してコンパイラーがそれぞれを区別できるようにしています。

Person オブジェクトはだんだん興味深いものになってきましたが、他にも動作が必要です。そのためには、さらにメソッドを追加する必要があります。

その他のメソッド

コンストラクターは特定の役割を持つ特殊なメソッドです。同様に、その他多くのタイプのメソッドも Java プログラム内で特定の役割を果たします。このセクションからチュートリアルの終わりまで、その他のさまざまなメソッドについて詳しく探ります。

リスト 1 でも目にしたように、メソッドを宣言するには以下のようにします。

accessSpecifier returnType methodName ([argumentList]) {
  methodStatement(s)
}

コンストラクター以外のメソッドもコンストラクターとほとんど同じように見えますが、いくつかの例外があります。その 1 つは、コンストラクター以外のメソッドには任意の名前を付けることができることです (ただし当然のことながら、特定の規則が適用されます)。以下の命名規則に従うことをお勧めします。

  • 小文字で始めること
  • どうしても必要という場合を除き、数字を使わないこと
  • 英字だけを使用すること

2 つ目の例外として、コンストラクターとは異なり、コンストラクター以外のメソッドでは必要に応じて「戻り値の型」を指定することができます。

Person のその他のメソッド

以上で説明した基本的な知識があれば、Person オブジェクトにいくつかのメソッドを追加したリスト 4 の内容を理解できるはずです (簡潔にするために、コンストラクターは省略しました)。

リスト 4. 新しいメソッドが追加された Person
package com.makotojava.intro;

public class Person {
   private String name;
   private int age;
   private int height;
   private int  weight;
   private String eyeColor;
   private String gender;

   public String getName() { return name; }
   public void setName(String value) { name = value; }
   // Other getter/setter combinations...
}

リスト 4 に含まれている、「getter/setter combinations」についてのコメントに注目してください。getter と setter は、あとでさらに追加することになります。今のところは、「getter」は属性の値を取得するためのメソッドであり、「setter」はその値を変更するためのメソッドであるということだけ覚えておいてください。リスト 4 には getter/setter の組み合わせが 1 つしかありませんが (Name 属性を対象としたもの)、同じような形でこの組み合わせをさらに定義できます。

リスト 4 の注として、メソッドが値を返さない場合は、そのことをコンパイラーに伝えるために、メソッドのシグニチャー内で戻り値の型として void を指定しなければならないことを付け加えておきます。

静的メソッドとインスタンス・メソッド

一般的に使用されている (コンストラクター以外の) メソッドには、「インスタンス・メソッド」と「静的メソッド」という 2 つのタイプがあります。インスタンス・メソッドの場合、その動作は特定のオブジェクト・インスタンスの状態によって決まります。静的メソッドは、1 つのオブジェクトの状態によってその動作が左右されないため、「クラス・メソッド」とも呼ばれています。静的メソッドは、クラス・レベルで動作します。

静的メソッドは主にユーティリティー用に使用されます。つまり静的メソッドは (C でのような) グローバル・メソッドであると見なせますが、静的メソッドのコードは、そのメソッドを定義するクラスと一緒に維持されます。

例えば、このチュートリアルでは情報をコンソールに出力するために一貫して JDK の Logger クラスを使用しますが、Logger クラスのインスタンスを作成するには Logger クラスをインスタンス化するのではなく、getLogger() という名前の静的メソッドを呼び出します。

クラスに対して静的メソッドを呼び出すための構文は、オブジェクトに対してメソッドを呼び出すために使用する構文とは異なります。また、以下の呼び出しに示されているように、静的メソッドの名前だけでなく、そのメソッドが含まれているクラスの名前も使用します。

Logger l = Logger.getLogger("NewLogger");

上記の例では、Logger がクラスの名前で、getLogger(...) が静的メソッドの名前です。このように、静的メソッドを呼び出す場合はオブジェクト・インスタンスを使用する必要はありません。静的メソッドはクラスの名前だけで呼び出すことができます。

初めての Java クラスを作成する

これまでのセクションで学んだ知識を実践に移し、コードの作成に取り掛かりましょう。このセクションでは Eclipse のパッケージ・エクスプローラーを使用して、Explorer クラスを宣言し、そのクラスに変数とメソッドを追加する手順を説明します。Logger クラスを使用してアプリケーションの動作を監視する方法、そして main() メソッドを テスト・ハーネス として使用する方法を学んでください。

パッケージを作成する

Eclipse のパッケージ・エクスプローラーがまだ表示されていない場合は、Eclipse で「Window (ウィンドウ)」 > 「Perspective (パースペクティブ)」 > 「Open Perspective (パースペクティブを開く)」の順に選択して、「Package Explorer (パッケージ・エクスプローラー)」を開いてください。パッケージ・エクスプローラーで、初めての Java クラスを作成するためのセットアップを行います。最初のステップは、クラスを存続させる場所を作成することです。パッケージは名前空間構成体であるだけでなく、便利なことに、ファイル・システムのディレクトリー構造に直接マッピングされます。

デフォルトのパッケージを使用するのではなく (ほとんどの場合、デフォルトのパッケージを使用するのは賢明なことではありません)、これから作成するコード専用のパッケージを作成します。それには、「File (ファイル)」 > 「New (新規)」 > 「Package (パッケージ)」の順にクリックして「Java Package (Java パッケージ)」ウィザードを開始します (図 4 を参照)。

図 4. Eclipse の「Java Package (Java パッケージ)」ウィザード
Eclipse の「Java Package (Java パッケージ)」ウィザードのスクリーンショット
Eclipse の「Java Package (Java パッケージ)」ウィザードのスクリーンショット

「Name (名前)」テキスト・ボックスに com.makotojava.intro と入力し、「Finish (完了)」をクリックします。「Package Explorer (パッケージ・エクスプローラー)」に、新しく作成されたパッケージが表示されます。

クラスを宣言する

パッケージ・エクスプローラーでクラスを作成する方法はいくつかありますが、最も簡単な方法は、作成したパッケージを右クリックして「New (新規)」 > 「Class... (クラス...)」を選択することです。これにより、「New Class (新規クラス)」ダイアログ・ボックスが開きます。

「Name (名前)」テキスト・ボックスに Person と入力してから「Finish (完了)」をクリックします。

編集ウィンドウ内に、新規クラスが表示されます。「Java Perspective (Java パースペクティブ)」を初めて開いたときにデフォルトで表示されるビュー (「Problems (問題)」、「Javadoc」など) は閉じることをお勧めします。そのほうが、作成するソース・コードが見やすくなります (デフォルト・ビューを閉じると、Eclipse はその操作を記憶するため、次回 Ecipse を開いて「Java Perspective (Java パースペクティブ)」を表示するときは、それらのデフォルト・ビューは開かれていない状態になります)。図 5 に、必要なビューが開かれた状態のワークスペースを示します。

図 5. 整頓されたワークスペース
Eclipse の「Java Package (Java パッケージ)」ウィザードの編集ウィンドウのスクリーンショット
Eclipse の「Java Package (Java パッケージ)」ウィザードの編集ウィンドウのスクリーンショット

Eclipse は自動的にシェル・クラスを生成して先頭に package 文を組み込みます。したがって、開発者はこのクラスを具体化していくだけでよいのです。Eclipse による新しいクラスの生成方法は、開発者が構成できます。それには、「Window (ウィンドウ)」 > 「Preferences (設定)」 > 「Java」 > 「Code Style (コード・スタイル)」 > 「Code Templates (コード・テンプレート)」を表示して、コード・テンプレートを構成します。ここでは単純にするために、Eclipse の既定のコード生成をそのまま受け入れます。

図 5 に示されている新しいソース・コード・ファイル名の横にアスタリスク (*) が示されていることに注意してください。このアスタリスクは、ファイルに変更が加えられたことを意味します。また、このコードはまだ保存されていないことにも注意してください。さらに、私が Name 属性を誤って宣言したことも指摘されています。Name の型を誤って Strin と宣言しているのです。このようなクラスの参照を見つけることはできないため、コンパイラーはコンパイル・エラーとしてフラグを立てています (つまり、Strin に赤い下線が引かれています)。もちろんこの誤りは、Strin の末尾に g を追加することで修正できます。このちょっとしたデモで、ソフトウェア開発にコマンドライン・ツールではなく IDE を使用するメリットがわかるはずです。エラーを修正して作業を進めるために、型を String に変更します。

クラス変数を追加する

リスト 3 で、Person クラスの具体化に着手しましたが、構文についてはほとんど説明しませんでした。ここで改めて、クラス変数を追加する方法を明確にします。

前述のとおり、変数には accessSpecifierdataTypevariableName、そして必要に応じて initialValue という属性があります。accessSpecifiervariableName を定義する方法についてはすでに簡単に説明したので、今度は変数に設定できる dataType について説明します。

dataType は、プリミティブ型または別のオブジェクトの参照にすることができます。例えば、Ageint (プリミティブ型) で、NameString (オブジェクト) です。JDK には java.lang.String のような有用なクラスがたくさん揃っています。また、java.lang パッケージに含まれるクラスはインポートする必要はありません (Java コンパイラーの省略表現のおかげです)。ただし、dataType が JDK のクラス (String など) であるかユーザー定義のクラスであるかに関わらず、構文は基本的に同じです。

表 1 に、頻繁に目にすることになる 8 種類のプリミティブ型と、メンバー変数の値を明示的に初期化しない場合にそれぞれのプリミティブ型が取るデフォルト値を記載します。

表 1. プリミティブ・データ型
サイズデフォルト値値の範囲
boolean適用外falsetrue または false
byte8 ビット0-128 ~ 127
char16 ビット(符号なし)\u0000' \u0000' ~ \uffff'、または 0 ~ 65535
short16 ビット0-32768 ~ 32767
int32 ビット0-2147483648 ~ 2147483647
long64 ビット0-9223372036854775808 ~ 9223372036854775807
float32 ビット0.01.17549435e-38 ~ 3.4028235e+38
double64 ビット0.04.9e-324 ~ 1.7976931348623157e+308

組み込みロギング

コードの詳細を掘り下げる前に、プログラムが現在の処理内容を通知する手段について知っておかなければなりません。

Java プラットフォームには、プログラムに関する情報を読んで理解できる形で収集するためのロギング・メカニズムとして、java.util.logging パッケージが組み込まれています。このパッケージに含まれる Logger クラスに対して静的メソッドを呼び出すことで、Logger という名前付きエンティティーを作成します。

import java.util.logging.Logger;
//...
Logger l = Logger.getLogger(getClass().getName());

getLogger() メソッドを呼び出すときには、String を渡します。差し当たり、作成中のコードを含めるクラスの名前を渡す習慣をつけてください。通常の (静的ではない) メソッドから Logger を呼び出す場合、上記のコードは常にクラスの名前を参照して、その名前を Logger に渡します。

静的メソッドから Logger を呼び出すとしたら、その静的メソッドが含まれているクラスの名前を参照します。

Logger l = Logger.getLogger(Person.class.getName());

上記の例では、作成しているコードは Person クラスに含まれるため、class という特殊なリテラルを参照します。このリテラルは、Class オブジェクト (あとで詳しく説明します) を検索して、そのオブジェクトの Name 属性を取得します。

このチュートリアルの「優れた Java コードを作成する方法」のセクションで、ロギングを「行わない」方法についてのヒントを紹介します。

実際のテストに取り掛かる前に、Eclipse のソース・コード・エディターで Person を表示し、以下のような内容になるように、リスト 3public class Person { の直後にコードを追加してください。

package com.makotojava.intro;

public class Person {
  private String name;
  private int age;
  private int height;
  private int weight;
  private String eyeColor;
  private String gender;
}

Eclipse には、(なかでもとりわけ) getter とsetter を生成するのに重宝なコード・ジェネレーターが備わっています。このコード・ジェネレーターを使ってみるには、マウスのポインターを Person クラス定義 (つまり、クラス定義に含まれる単語 Person) の上に重ねて表示されるメニューで「Source (ソース)」 > 「Generate Getters and Setters... (getter と setter を生成...)」をクリックします。ダイアログ・ボックスが開いたら、「Select All (すべて選択)」をクリックします (図 6 を参照)。

図 6. Eclipse の「Generate Getters and Setters (getter と setter の生成)」ダイアログ・ボックス
Eclipse の「Generate Getters and Setters (getter と setter の生成)」ダイアログ・ボックスのスクリーンショット
Eclipse の「Generate Getters and Setters (getter と setter の生成)」ダイアログ・ボックスのスクリーンショット

挿入ポイントとして「Last member (最後のメンバー)」を選択し、「OK」をクリックします。

次は、コンストラクターを Person に追加します。それには、ソース・ウィンドウで、リスト 5 のコードをクラス定義の先頭部分のすぐ下 (public class Person () の直下の行) に入力します。

リスト 5. Person コンストラクター
public Person(String name, int age, int height, int weight, String eyeColor, String gender) {
  this.name = name;
  this.age = age;
  this.height = height;
  this.weight = weight;
  this.eyeColor = eyeColor;
  this.gender = gender;
}

コンパイル・エラーを示す波線が表示されていないことを確認してください。

JUnit テスト・ケースを生成する

次は、JUnit テスト・ケースを生成して、そのテスト・ケースでリスト 5 のコンストラクターを使用して Person をインスタンス化し、オブジェクトの状態をコンソールに出力します。つまり、この「テスト」によって、コンストラクターの呼び出しで属性の順序が正しい (つまり、正しい属性に設定される) ことを確認します。

「Package Explorer (パッケージ・エクスプローラー)」で、Person クラスを右クリックし、「New (新規)」 > 「JUnit Test Case (JUnit テスト・ケース)」をクリックします。「New JUnit Test Case (新規 JUnit テスト・ケース)」ウィザードの最初のページが開きます (図 7 を参照)。

図 7. JUnit テスト・ケースを作成する
JUnit テスト・ケースを作成するダイアログ・ボックスの 1 ページ目のスクリーンショット
JUnit テスト・ケースを作成するダイアログ・ボックスの 1 ページ目のスクリーンショット

デフォルト値を受け入れて「Next (次へ)」をクリックします。「Test Methods (メソッドのテスト)」ダイアログ・ボックスが表示されます (図 8 を参照)。

図 8. ウィザードでテスト・ケースを生成する対象のメソッドを選択
JUnit テスト・ケースを作成するための「Test Methods (メソッドのテスト)」ダイアログ・ボックスのスクリーンショット
JUnit テスト・ケースを作成するための「Test Methods (メソッドのテスト)」ダイアログ・ボックスのスクリーンショット

このダイアログ・ボックスで、ウィザードにテストを作成させる対象のメソッドを 1 つ以上選択できます。この例では、コンストラクターだけを選択します (図 8 を参照)。「Finish (完了)」をクリックすると、Eclipse によって JUnit テスト・ケースが生成されます。

次に、PersonTest を開き、testPerson() メソッドをリスト 6 に示すように変更します。

リスト 6. testPerson() メソッド
@Test
public void testPerson() {
  Person p = new Person("Joe Q Author", 42, 173, 82, "Brown", "MALE");
  Logger l = Logger.getLogger(Person.class.getName());
  l.info("Name: " + p.getName());
  l.info("Age:" + p.getAge());
  l.info("Height (cm):" + p.getHeight());
  l.info("Weight (kg):" + p.getWeight());
  l.info("Eye Color:" + p.getEyeColor());
  l.info("Gender:" + p.getGender());
  assertEquals("Joe Q Author", p.getName());
  assertEquals(42, p.getAge());
  assertEquals(173, p.getHeight());
  assertEquals(82, p.getWeight());
  assertEquals("Brown", p.getEyeColor());
  assertEquals("MALE", p.getGender());
}

Logger クラスについてはとりあえず無視して問題ありません。リスト 6 に記載されているとおりにコードを入力してください。これで、初めて作成した Java クラス (そして JUnit テスト) を実行する準備が整いました。

Eclipse 内で単体テストを実行する

Eclipse の「Package Explorer (パッケージ・エクスプローラー)」で、PersonTest.java を右クリックし、 「Run As (実行)」 > 「JUnit Test (JUnit テスト)」 の順に選択します。これにより、図 9 に示すような内容が表示されます。

図 9. Person 実行状況の表示
Person を Java アプリケーションとして実行中の Eclipse のスクリーンショット
Person を Java アプリケーションとして実行中の Eclipse のスクリーンショット

「Console (コンソール)」ビューが自動的に開いて Logger の出力が表示されます。JUnit のビューには、テストの実行がエラーなしで完了したことが示されます。

Java クラスに動作を追加する

Person は次第に具体的になってきましたが、他の動作も使用することでさらに興味深いものにすることができます。動作を作成するということは、すなわちメソッドを追加するということです。このセクションでは「アクセサー・メソッド」、つまり、先ほど動作を確認した getter と setter についてさらに詳しく見ていきます。

アクセサー・メソッド

前のセクションの終わりで動作を確認した getter と setter は、「アクセサー・メソッド」と呼ばれています (簡単なおさらい: 「getter」は属性の値を取得するためのメソッドであり、「setter」はその値を変更するためのメソッドです)。他のオブジェクトからのクラスのデータをカプセル化するには、そのオブジェクトの変数を private として宣言してからアクセサー・メソッドを指定します。

アクセサー・メソッドの命名は、「JavaBeans パターン」として知られる厳格な規則に従います。このパターンでは、あらゆる Foo 属性に getFoo() という名前の getter と setFoo() という名前の setter を使用します。JavaBeans パターンは広く普及しているため、Person の getter と setter を生成したときに見たように、Eclipse IDE にはこのパターンのサポートが統合されています。

アクセサーは以下のガイドラインに従います。

  • 属性は常に private アクセスとして宣言されます。
  • getter と setter のアクセス修飾子は public です。
  • getter はパラメーターを取らず、アクセス先の属性と同じ型を持つ値を返します。
  • setter は属性の型だけをパラメーターとして取り、値は返しません。

アクセサーを宣言する

アクセサーを宣言するのに最も簡単な方法は、間違いなく Eclipse に自動的に宣言させることです。そうは言っても、getter と setter を手作業でコーディングするにはどのようにするのかを知っていなければなりません。

例えば、Foo という属性があり、その型は java.lang.String だとします。私が (アクセサーのガイドラインに従って) 作成した Foo の完全な宣言は以下のとおりです。

private String foo;
public String getFoo() {
  return foo;
}
public void setFoo(String value) {
  foo = value;
}

setter に渡すパラメーター値には、Eclipse で生成した場合の値とは異なる名前を指定していることに注意してください (例えば、public void setFoo(String foo) と指定します)。まれに setter を手作業でコーディングする際に、私は setter に渡すパラメーター値の名前として常に value を使用するようにしています。このような目を引く名前にしておけば (私自身の規則であり、私が他の開発者にも推奨している規則)、setter を手作業でコーディングしたことを忘れることはありません。Eclipse に自動的に getter と setter を生成させない場合、それには正当な理由があります。setter のパラメーター値として value を使用することで、その setter が特殊なものであることを再認識できます (コードのコメントでも、同じ目的を果たすことができます)。

メソッドを呼び出す

メソッドを起動する (「呼び出す」) のは簡単です。例えばリスト 6testPerson メソッドは Person のさまざまな getter を呼び出して、それぞれの値を受け取ります。これから、メソッドを呼び出す正式な仕組みを説明します。

パラメーターを使用する場合と使用しない場合のメソッド呼び出し

オブジェクトに対してメソッドを呼び出すには、そのオブジェクトの参照が必要です。メソッドを呼び出すための構文は、以下の要素からなります。

  • オブジェクト参照
  • リテラル・ドット
  • メソッド名
  • メソッドに渡す必要があるパラメーター

パラメーターを使用しない場合のメソッド呼び出しの構文は以下のとおりです。

objectReference.someMethod();

パラメーターを使用しないメソッド呼び出しの一例を記載します。

Person p = new Person("Joe Q Author", 42, 173, 82, "Brown", "MALE");
p.getName();

パラメーターを使用する場合のメソッド呼び出しの構文は以下のとおりです。

objectReference.someOtherMethod(parameter1, parameter2, . . ., parameterN);

パラメーターを使用したメソッド呼び出しの一例 (PersonName 属性を設定する場合) を記載します。

Person p = new Person("Joe Q Author", 42, 173, 82, "Brown", "MALE");
p.setName("Jane Q Author");

コンストラクターもメソッドであることを思い出してください。各パラメーターはスペースと改行で区切ることができます。どちらを使っても、Java コンパイラーは区別しません。したがって、以下の 2 つのメソッド呼び出しは同等です。

new Person("Joe Q Author", 42, 173, 82, "Brown", "MALE");
new Person("Joe Q Author",// Name
  42,     // Age
  173,    // Height in cm
  82,     // Weight in kg
  "Brown",// Eye Color
  "MALE");// Gender

2 番目のコンストラクター呼び出しではコメントを使用して、今後このコードを扱うかもしれない開発者のためにコードを読みやすくしています。このようになっていれば、各パラメーターの意図を一目で理解できます。

ネストされたメソッド呼び出し

以下のように、メソッド呼び出しをネストすることもできます。

Logger l = Logger.getLogger(Person.class.getName());
l.info("Name: " + p.getName());

上記の例では、Person.class.getName() の戻り値が getLogger() メソッドに渡されます。getLogger() メソッド呼び出しは、静的メソッドの呼び出しであるため、構文がわずかに異なることに注意してください (メソッドを呼び出すために Logger を参照する必要はありません。Logger の参照ではなく、呼び出し構文の等式の左辺でクラスの名前を使用します)。

メソッド呼び出しについての説明は以上です。

ストリングと演算子

このチュートリアルではこれまでに String 型の変数をいくつか取り上げましたが、説明はほとんどしていません。このセクションで、ストリングについて詳しく説明すると同時に、演算子を使用する場合とその方法について説明します。

ストリング

Java 言語では、ストリングは String 型のファーストクラス・オブジェクトであり、さまざまなメソッドを利用して操作できます。

以下に String を作成する 2 つの方法を示します。これらの例では、値 hello を設定した greeting という名前の String インスタンスを作成しています。

greeting = new String("hello");
String greeting = "hello";

String はファーストクラス・オブジェクトであるため、new を使用してインスタンス化できます。String 型の変数をストリング・リテラルに設定しても同じ結果になります。それは、Java 言語ではリテラルを保持する String オブジェクトを作成してから、そのオブジェクトにインスタンス変数を割り当てるためです。

ストリングを連結する

String を使用することでさまざまな処理が可能であり、このクラスにはそのために役立つメソッドが多数あります。たとえメソッドを使用しなくても、すでに Person クラスの testPerson() メソッド内で 2 つの String を連結 (結合) するという興味深いことを行っています。

l.info("Name: " + p.getName());

プラス (+) 記号は、Java 言語では Srting を連結するための省略表現です (ループ内でこのタイプの連結を行うとパフォーマンスに影響を与えますが、差し当たり、それについて心配する必要はありません)。

連結の演習

Person クラス内でさらに 2 つの String を連結する演習にチャレンジしてください。現時点では name インスタンス変数を使用していますが、ビジネス・アプリケーション内では firstNamelastName を使用したほうがより現実的です。その場合、別のオブジェクトが Person のフルネームを要求したときに、この 2 つを連結して要求に応じることができます。

Eclipse プロジェクトに戻って、新しいインスタンス変数を追加する作業に取り掛かってください (ソース・コードで現在 name が定義されている場所に追加します)。

//private String name;
private String firstName;
private String lastName;

上記に示されているように、name 定義をコメントアウトします。namefirstNamelastName で置き換えると、この定義は不要になるためです。

メソッド呼び出しをチェーニングする

次は、Eclipse コード・ジェネレーターに対し、firstName および lastName の getter と setter をそれぞれ生成するように指示します (必要に応じて「初めての Java クラスを作成する」のセクションを参照してください)。その後、setName() および getName() メソッドを削除して、以下のような getFullName() メソッドを新しく追加します。

public String getFullName() {
  return getFirstName().concat(" ").concat(getLastName());
}

上記のコードは、メソッド呼び出しを「チェーニング」する例を示しています。チェーニングは、String のような不変オブジェクトでよく使われる手法であり、メソッド呼び出しのチェーニングでは、不変オブジェクトに変更を加えると常にその変更が返されます (ただし、元のオブジェクトは変更されません)。そして返された変更後の値に対して処理を行うという手法です。

演算子

すでに目にしたように、Java 言語では常に = 演算子を使用して変数に値を代入します。お察しのとおり、Java 言語は計算に対応可能であり、算術演算子も使用します。ここで、スキルが上がっていくうちに必要になってくる Java 言語の演算子をいくつか取り上げて簡単に説明します。

Java 言語では、以下の 2 つのタイプの演算子を使用します。

  • 単項演算子: 必要なオペランドは 1 つだけです。
  • 二項演算子: 2 つのオペランドが必要です。

表 2 に、Java 言語の算術演算子を要約します。

表 2. Java 言語の算術演算子
演算子用途説明
+a + bab を加算します。
++aa の型が byteshort、または char の場合、aint にプロモートします。
-a - ba から b を減算します。
--a算術的に a を否定します。
*a * bab を乗算します。
/a / bab で除算します。
%a % bab で除算した余りを返します (モジュロ演算子)。
++a++a を 1 でインクリメントし、インクリメントする前の a の値を計算します。
++++aa を 1 でインクリメントし、インクリメントした後の a の値を計算します。
--a--a を 1 でデクリメントし、デクリメントする前の a の値を計算します。
----aa を 1 でデクリメントし、デクリメントした後の a の値を計算します。
+=a += ba = a + b の省略表現
-=a -= ba = a - b の省略表現
*=a *= ba = a * b の省略表現
%=a %= ba = a % b の省略表現
その他の演算子

表 2 に記載されている演算子の他に、Java 言語で演算子と呼ばれている記号がいくつかあります。以下はその数例です。

  • ピリオド (.): パッケージの名前を修飾してメソッドを呼び出します。
  • 括弧 (()): メソッドに渡すパラメーターのコンマ区切りリストを区切ります。
  • new: (この後にコンストラクター名が続く場合) オブジェクトをインスタンス化します。

Java 言語の構文には、条件付きプログラミング (つまり、入力に応じて異なる応答を返すプログラム) に特化した演算子もいくつか含まれています。次のセクションで、それらの演算子について説明します。

条件演算子と制御文

このセクションでは、Java プログラムに対し、異なる入力に基づく動作方法を指示するために使用できる各種の文と演算子について説明します。

比較演算子と条件演算子

Java 言語では演算子と制御文を使って、コード内で決定を行うことができます。ほとんどの場合、コード内での決定は「ブール式」で始まります。ブール式とは、true または false に評価される式のことです。このような式では、比較演算子 (一方のオペランドを他方のオペランドと比較する演算子) および条件演算子を使用します。

表 3 に、Java 言語の比較演算子と条件演算子を記載します。

表 3. 比較演算子と条件演算子
演算子用途true を返す条件
>a > bab より大きい
>=a >= bab と同じか、b より大きい
<a < bab より小さい
<=a <= bab と同じか、b より小さい
==a == bab と等しい
!=a != bab と等しくない
&&a && bab の両方が true の場合、条件付きで b を評価 (a が false の場合、b は評価されません)
||a || bab のいずれかが true の場合、条件付きで b を評価 (a が true の場合、b は評価されません)
!!aa が false
&a & bab の両方が true の場合、常に b を評価
|a | bab のいずれかが true の場合、常に b を評価
^a ^ bab が異なる

if 文

さまざまな演算子があることがわかったところで、実際に演算子を使用してみましょう。以下のコードは、Person オブジェクトの getHeight() アクセサーにいくらかのロジックを追加するとどうなるのかを示しています。

public int getHeight() {
  int ret = height;
  // If locale of the computer this code is running on is U.S.,
  if (Locale.getDefault().equals(Locale.US))
    ret /= 2.54;// convert from cm to inches
  return ret;
}

現在のロケールが米国に設定されている場合は、(米国ではメートル法が使用されていないため) height の内部値 (センチメートル単位) をインチ単位に変換することが理にかなっています。上記の (いくぶん不自然な) 例には、if 文の使用方法が示されています。if 文は括弧で囲まれたブール式を評価し、式が true に評価されると、プログラムが次の文を実行します。

この例では、コードを実行しているコンピューターの LocaleLocale.US に設定されている場合、1 つの文を実行すればよいだけですが、複数の文を実行する必要がある場合は、それらの文を中括弧で囲んで複合文という形にします。複合文は多数の文を 1 つにグループ化する文であり、複合文には他の複合文を含めることもできます。

変数のスコープ

Java アプリケーション内で使用する変数には例外なく「スコープ」(ローカライズされた名前空間) が設定されます。スコープが設定されることで、コード内では変数にその名前でアクセスすることが可能になります。ローカライズされた名前空間の外部では変数はスコープを外れるため、外部から変数にアクセスしようとするとコンパイル・エラーが返されます。Java 言語では、変数を宣言する場所によってスコープのレベルが定義されます (リスト 7 を参照)。

リスト 7. 変数のスコープ
public class SomeClass {
  private String someClassVariable;
  public void someMethod(String someParameter) {
    String someLocalVariable = "Hello";

    if (true) {
      String someOtherLocalVariable = "Howdy";
    }
    someClassVariable = someParameter; // legal
    someLocalVariable = someClassVariable; // also legal
    someOtherLocalVariable = someLocalVariable;// Variable out of scope!
  }
  public void someOtherMethod() {
    someLocalVariable = "Hello there";// That variable is out of scope!

  }
}

SomeClass の内部であれば、すべてのインスタンス・メソッド (つまり、静的メソッドではないメソッド) が someClassVariable にアクセスできます。someParametersomeMethod メソッド内では可視になりますが、このメソッドの外部では可視になりません。これは、someLocalVariable にも該当します。someOtherLocalVariableif ブロック内で宣言されているため、if ブロックの外部ではスコープを外れます。つまり、{} で区切られたブロックがスコープの境界を定義することから、Java では「ブロック・スコープ」を使用すると言えます。

スコープには多数の規則がありますが、リスト 7 に示されている規則が最も一般的なものです。少々時間を割いて、これらの一般的な規則を十分に理解してください。

else 文

時には、プログラムの制御フローの中で、ある特定の式が true に評価されなかった場合にだけアクションを実行したいことがあります。そのようなときに重宝するのが、else です。

public int getHeight() {
  int ret;
  if (gender.equals("MALE"))
    ret = height + 2;
  else {
    ret = height;
    Logger.getLogger("Person").info("Being honest about height...");
  }
  return ret;
}

プログラムは次に検出した文だけを実行するという点で、else 文は if 文と同じように機能します。上記の例では、2 つの文がグループ化されて複合文になっているため (中括弧に注意してください)、プログラムはこの 2 つの複合文を実行します。

else を使用して if の追加チェックを行うこともできます。

if (conditional) {
  // Block 1
} else if (conditional2) {
  // Block 2
} else if (conditional3) {
  // Block 3
} else {
  // Block 4
} // End

conditionaltrue に評価された場合は Block 1 が実行され、プログラムは最後の中括弧 (// End で示されています) に続く次の文までジャンプします。conditionaltrue に評価されなければ、conditional2 が評価されます。conditional2true に評価されると、Block 2 が実行され、プログラムは最後の中括弧に続く次の文までジャンプします。conditional2true でなければ、プログラムは conditional3 に進むといった具合です。3 つの conditional のすべてが true でない場合にのみ、Block 4 が実行されます。

三項演算子

Java 言語には、単純な if / else 文のチェックを行うのに便利な演算子があります。その演算子の構文は以下のとおりです。

(conditional) ? statementIfTrue : statementIfFalse;

conditionaltrue に評価されると statementIfTrue が実行され、そうでなければ statementIfFalse が実行されます。いずれの文にも、複合文は使用できません。

このような三項演算子が役立つのは、条件が true に評価されると一方の文を実行し、true に評価されなければもう一方の文を実行しなければならないことがわかっている場合です。三項演算子は一般に、以下のように変数 (戻り値など) を初期化するために使用されます。

public int getHeight() {
  return (gender.equals("MALE")) ? (height + 2) : height;
}

疑問符の後の括弧は絶対に必要というわけではありませんが、括弧を使用するとコードが読みやすくなります。

ループ

プログラムに条件を適用して、さまざまな if/then シナリオに基づいて異なる結果を出せるだけでなく、ジョブが完了するまでコードに同じことを繰り返し実行させることもできます。このセクションでは、コードを繰り返す場合、またはコードを複数回実行する場合に使用する構成体について説明します。

ループとは何か

ループとは、特定の条件 (または一連の条件) を満たすまで繰り返し実行するプログラミング構成体のことです。例えば、プログラムにデータ・ファイルの終わりに達するまですべてのレコードを読み取らせたり、配列に含まれる要素を 1 つずつ処理させたりする場合には、ループを使用します (配列については、次のセクションで説明します)。

以下の 3 つのループ構成体によって、コードを繰り返したり、コードを複数回実行したりすることが可能になります。

  • for ループ
  • while ループ
  • do...while ループ

for ループ

Java 言語での基本的なループ構成体は、for 文です。for 文を使用することで、一定の範囲の値を繰り返し処理し、ループを何回実行するかを決定できます。for ループの抽象構文は以下のとおりです。

for (initialization; loopWhileTrue; executeAtBottomOfEachLoop) {
  statementsToExecute
}

ループの開始時に、初期化文が実行されます (各初期化文をコンマで区切ることで、複数の初期化文を使用できます)。loopWhileTrue (true または false に評価される必要がある Java の条件式) が true であれば、ループが実行されます。ループの最後には、executeAtBottomOfEachLoop が実行されます。

例えば、リスト 8 に記載する main() メソッドに含まれるコードを 3 回実行するには、以下の for ループを使用できます。

リスト 8. for ループ
public static void main(String[] args) {
  Logger l = Logger.getLogger(Person.class.getName());
  for (int aa = 0; aa < 3; aa++) 
    Person p = new Person("Joe Q Author", 42, 173, 82, "Brown", "MALE");
    l.info("Loop executing iteration# " + aa);
    l.info("Name: " + p.getName());
    l.info("Age:" + p.getAge());
    l.info("Height (cm):" + p.getHeight());
    l.info("Weight (kg):" + p.getWeight());
    l.info("Eye Color:" + p.getEyeColor());
    l.info("Gender:" + p.getGender());
  }
}

リスト 8 では、最初にローカル変数 aa がゼロに初期化されます。この文は、ループが初期化される際に 1 回だけ実行されます。初期化に続き、ループが 3 回繰り返され、ループの繰り返しごとに aa が 1 ずつインクリメントされます。

次のセクションで説明しますが、for ループ構文ではなく、Iterable インターフェースを実装する構成体 (配列やその他の Java ユーティリティー・クラス) を使用してループ処理を行うこともできます。今のところは、リスト 8 に示されている for ループ構文の使い方だけを覚えておいてください。

while ループ

while ループの構文は以下のとおりです。

while (condition) {
  statementsToExecute
}

推測できると思いますが、このループは条件が true に評価されると実行されます。条件は毎回繰り返しが開始されるときに (つまり、文が実行される前に) 評価されます。ループが実行されるのは条件が true に評価された場合のみです。したがって、条件式が一度も true に評価されなければ、while ループはまったく実行されません。

リスト 8for ループをもう一度見てください。比較のために、リスト 9 では while ループを使用して同じ結果を出しています。

リスト 9. while ループ
public static void main(String[] args) {
  Logger l = Logger.getLogger(Person.class.getName());
  int aa = 0;
  while (aa < 3) {
    Person p = new Person("Joe Q Author", 42, 173, 82, "Brown", "MALE");
    l.info("Loop executing iteration# " + aa);
    l.info("Name: " + p.getName());
    l.info("Age:" + p.getAge());
    l.info("Height (cm):" + p.getHeight());
    l.info("Weight (kg):" + p.getWeight());
    l.info("Eye Color:" + p.getEyeColor());
    l.info("Gender:" + p.getGender());
    aa++;
  }
}

ご覧のように、while ループに必要なハウスキーピング処理は for ループよりやや多めです。while ループの場合、aa 変数を初期化するだけでなく、ループの最後でこの変数をインクリメントすることも忘れないでください。

do...while ループ

必ず 1 回実行された後にその条件式をチェックするループが必要な場合は、do...while ループを使用できます (リスト 10 を参照)。

リスト 10. do...while ループ
int aa = 0;
do {
  Person p = new Person("Joe Q Author", 42, 173, 82, "Brown", "MALE");
  l.info("Loop executing iteration# " + aa);
  l.info("Name: " + p.getName());
  l.info("Age:" + p.getAge());
  l.info("Height (cm):" + p.getHeight());
  l.info("Weight (kg):" + p.getWeight());
  l.info("Eye Color:" + p.getEyeColor());
  l.info("Gender:" + p.getGender());
  aa++;
} while (aa < 3);

条件式 (aa < 3) はループの終わりに達するまでチェックされません。

ループの終了

場合によっては、条件式が false に評価される前にループから抜ける (終了する) 必要があることもあります。例えば、String からなる配列で特定の値を検索する場合、その値が見つかれば、配列の他の要素を調べても意味はありません。ループを脱出する必要がある場合に備えて、Java 言語には break 文が用意されています (リスト 11 を参照)。

リスト 11. break
public static void main(String[] args) {
  Logger l = Logger.getLogger(Person.class.getName());
  int aa = 0;
  while (aa < 3) {
    if (aa == 1)
      break;
    Person p = new Person("Joe Q Author", 42, 173, 82, "Brown", "MALE");
    l.info("Loop executing iteration# " + aa);
    l.info("Name: " + p.getName());
    l.info("Age:" + p.getAge());
    l.info("Height (cm):" + p.getHeight());
    l.info("Weight (kg):" + p.getWeight());
    l.info("Eye Color:" + p.getEyeColor());
    l.info("Gender:" + p.getGender());
    aa++;
  }
}

break 文は、その文が配置されているループの外部にある、次に実行可能な文に処理を進めます。

ループの継続

リスト 11 の (極めて単純化した) 例では、ループを 1 回だけ実行した後、ループから脱出するようにしていますが、ループの繰り返し処理を 1 回スキップしながらも、ループの実行は継続されるようにすることもできます。それには、continue 文が必要です (リスト 12 を参照)。

リスト 12. continue
public static void main(String[] args) {
  Logger l = Logger.getLogger(Person.class.getName());
  int aa = 0;
  while (aa < 3) {
    aa++;
    if (aa == 2)
      continue;
    Person p = new Person("Joe Q Author", 42, 173, 82, "Brown", "MALE");
    l.info("Loop executing iteration# " + aa);
    l.info("Name: " + p.getName());
    l.info("Age:" + p.getAge());
    l.info("Height (cm):" + p.getHeight());
    l.info("Weight (kg):" + p.getWeight());
    l.info("Eye Color:" + p.getEyeColor());
    l.info("Gender:" +
    p.getGender());
  }
}

リスト 12 ではループの 2 回目の繰り返しをスキップして、3 回目の繰り返しを続けるようになっています。continue が役立つのは、例えばレコードを処理している途中で処理したくないレコードが見つかった場合です。continue 文を使用すれば、そのレコードをスキップして、次のレコードに処理を進めることができます。

Java のコレクション

実際の世界に即したアプリケーションは、一連のファイルや変数、ファイルからのレコード、あるいはデータベースの結果セットなどといったコレクションを扱います。Java 言語では高度な Collections Framework を使用して、さまざまなタイプのオブジェクトのコレクションを作成し、管理することができます。このセクションでは、最もよく使われているコレクション・クラスを紹介し、それらを使用する方法を説明します。

配列

ほとんどのプログラミング言語には、要素の集合を格納する配列の概念が含まれていますが、Java 言語もその例外ではありません。配列は基本的に、同じ型の要素のコレクションです。

配列を宣言するには、以下のいずれかの方法を使用できます。

  • 特定のサイズを設定した配列を作成します。そのサイズは、配列の有効期間にわたって変わることはありません。
  • 一連の初期値を設定した配列を作成します。これらの初期値によって、配列のサイズが決まります。サイズは初期値のすべてをちょうど格納できるだけの大きさであり、そのサイズは配列の有効期間にわたって変わることはありません。

配列を宣言する

通常は、以下のように配列を宣言します。

new elementType [arraySize]

要素の整数配列を作成するには、2 つの方法があります。まず 1 つの方法として、以下の文は 5 個の要素を格納するスペースを確保した配列を空の状態で作成します。

// creates an empty array of 5 elements:
int[] integers = new int[5];

以下の文は、配列を作成して、まとめて一度に初期化します。

// creates an array of 5 elements with values:
int[] integers = new int[] { 1, 2, 3, 4, 5 };

または

// creates an array of 5 elements with values (without the new operator):
int[] integers = { 1, 2, 3, 4, 5 };

初期値を中括弧の中に入れ、各値をコンマで区切ります。

配列を作成するもう 1 つの方法は、配列を作成してから、その配列を初期化するためのループをコーディングすることです。

int[] integers = new int[5];
for (int aa = 0; aa < integers.length; aa++) {
  integers[aa] = aa+1;
}

上記のコードは、5 個の要素からなる整数配列を宣言しています。5 個よりも多くの要素を配列に含めようとすると、Java ランタイムが例外をスローします。例外とその処理方法については、第 2 回で説明します。

配列をロードする

配列をロードするには、1 から配列の長さまでの整数をループ処理します (配列の長さは、配列に対して .length を呼び出すことで確認できます。これについては、この後すぐに説明します)。以下の例では、5 に達した時点でループ処理を停止します。

配列がロードされた後は、前と同じようにして配列にアクセスできます。

Logger l = Logger.getLogger("Test");
for (int aa = 0; aa < integers.length; aa++) {
  l.info("This little integer's value is: " + integers[aa]);
}

以下の構文も有効です (このほうが扱いやすいので) このセクションでは一貫してこの構文を使用します。

Logger l = Logger.getLogger("Test");
for (int i : integers) {
  l.info("This little integer's value is: " + i);
}

要素のインデックス

配列は、一連のバケツのようなもので、バケツごとに特定の型の要素が入ると考えてください。各バケツにアクセスするには、要素の「インデックス」を使用します。

element = arrayName [elementIndex];

要素にアクセスするには、配列の参照 (配列の名前) と、目的の要素が含まれているインデックスが必要です。

length 属性

すべての配列には、public アクセスが設定された length 属性があります。この属性を使用して、配列内に格納できる要素の数を調べることができます。length 属性にアクセスするには、以下のように配列の参照、ドット (.)、単語 length を使用します。

int arraySize = arrayName.length;

Java 言語での配列は、「ゼロ・ベース」です。つまり、どの配列でも、そこに含まれる最初の要素は常に arrayName[0] に位置し、最後の要素は arrayName[arrayName.length - 1] に位置します。

オブジェクトの配列

配列にプリミティブ型を格納する方法について説明しましたが、配列にはオブジェクトを格納することもできます。java.lang.Integer オブジェクトの配列を作成する方法は、プリミティブ型の配列を作成する方法とそれほど変わりません。この場合も、以下の 2 つの方法があります。

// creates an empty array of 5 elements:
Integer[] integers = new Integer[5];
// creates an array of 5 elements with values:
Integer[] integers = new Integer[] {
Integer.valueOf(1),
Integer.valueOf(2),
Integer.valueOf(3),
Integer.valueOf(4),
Integer.valueOf(5)
};

ボックス化とボックス化解除

表 4 に記載するように、JDK には、Java 言語でのプリミティブ型のそれぞれに対応するクラスがあります。

表 4. プリミティブと JDK での対応するクラス
プリミティブJDK での対応するクラス
booleanjava.lang.Boolean
bytejava.lang.Byte
charjava.lang.Character
shortjava.lang.Short
intjava.lang.Integer
longjava.lang.Long
floatjava.lang.Float
doublejava.lang.Double

JDK の各クラスには、その内部表現を解析して対応するプリミティブ型に変換するためのメソッドが用意されています。一例として、以下のコードは 10 進値を Integer に変換します。

int value = 238;
Integer boxedValue = Integer.valueOf(value);

プリミティブ型をラッパー、つまりボックスに代入することから、この手法は「ボックス化」と呼ばれています。

同様に、Integer 表現を対応する int 型に変換するには、ボックス化解除します。

Integer boxedValue = Integer.valueOf(238);
int intValue = boxedValue.intValue();

オートボクシングとオートアンボクシング

厳密に言うと、プリミティブ型を明示的にボックス化、ボックス化解除する必要はありません。それは、Java 言語のオートボクシング機能とオートアンボクシング機能を使用できるためです。

int intValue = 238;

Integer boxedValue = intValue;
//
intValue = boxedValue;

ただし、コードの読みやすさに問題が生じる可能性があることから、オートボクシングとオートアンボクシングは使用しないようにすることをお勧めします。ボックス化およびボックス化解除するスニペットではコードの内容がより明らかになるため、オートボクシング機能が適用されたコードよりも理解しやすくなります。したがって、作業を追加する価値はあるはずです。

ボックス化された型の解析と変換

型をボックス化する方法は説明しましたが、ボックス化された型があるかどうか疑わしい数値の String を解析して正しいボックスに入れるにはどうすればよいでしょうか? JDK のラッパー・クラスには、そのためのメソッドもあります。

String characterNumeric = "238";
Integer convertedValue = Integer.parseInt(characterNumeric);

JDK のラッパー・タイプの内容を String に変換することもできます。

Integer boxedValue = Integer.valueOf(238);
String characterNumeric = boxedValue.toString();

(Logger の呼び出しで見たように) String の式の中で連結演算子を使用すると、プリミティブ型にオートボクシング機能が適用されて、ラッパー・タイプに対して自動的に toString() が呼び出されるので、かなり便利です。

リスト

List は順序付きコレクションであり、「シーケンス」とも呼ばれています。List は順序付きであることから、List 項目を代入する場所を完全に制御できます。Java の List コレクションに格納できるのはオブジェクトだけで (int のようなプリミティブ型を格納することはできません)、動作に関して厳格な規則が定義されます。

List はインターフェースなので、直接インスタンス化することはできません (インターフェースについては、第 2 回で説明します)。ここで、最もよく使われている List の実装である ArrayList を処理しましょう。これを宣言するには、2 つの方法があります。最初の方法では、明示的な構文を使用します。

List<String> listOfStrings = new ArrayList<String>();

2 番目の方法では「ダイヤモンド」演算子を使用します (この演算子は JDK 7 で導入されました)。

List<String> listOfStrings = new ArrayList<>();

ArrayList のインスタンス化で、オブジェクトの型が指定されていないことに注意してください。式の右辺にあるクラスの型が左辺の型と一致しなければならない場合は、オブジェクトの型を指定しません。実際のプログラミングでは上記のどちらの方法も目にするはずなので、チュートリアルの残りでは、両方の方法を使用します。

上記で、私が ArrayList オブジェクトを List 型の変数に代入したことに注意してください。Java プログラミングで、ある変数の型を別の型に代入できるのは、代入先の変数が、代入元の変数によって実装されるスーパークラスまたはインターフェースである場合です。後のほうのセクションで、このようなタイプの変数代入を統制する規則について詳しく説明します。

仮型

上記のコード・スニペットで使用されている <Object> は、「仮型」と呼ばれます。<Object> はコンパイラーに対し、この List に含まれるのは Object 型のコレクションであると指示します。つまり、List にはほとんど何でも格納できます。

List に格納できる要素に関する制約を厳しくしたいとしたら、仮型を次のように定義することもできます。

List<Person> listOfPersons = new ArrayList<Person>();

これで、List に格納できる要素は Person インスタンスに限定されます。

List を使用する

List を使用するのは (Java のコネクション全般に言えることですが) ごく簡単です。List では、例えば以下の処理を実行できます。

  • List に何らかの要素を格納する
  • List に現在の自身の大きさを尋ねる
  • List から何らかの要素を取得する

List に要素を格納する場合は、add() メソッドを呼び出します。

List<Integer> listOfIntegers = new ArrayList<>();
listOfIntegers.add(Integer.valueOf(238));

add() メソッドは要素を List の最後に追加します。

List に現在の自身の大きさを尋ねる場合は、size() メソッドを呼び出します。

List<Integer> listOfIntegers = new ArrayList<>();

listOfIntegers.add(Integer.valueOf(238));
Logger l = Logger.getLogger("Test");
l.info("Current List size: " + listOfIntegers.size());

List から要素を取得する場合は、get() を呼び出して、取得する要素のインデックスを渡します。

List<Integer> listOfIntegers = new ArrayList<>();
listOfIntegers.add(Integer.valueOf(238));
Logger l = Logger.getLogger("Test");
l.info("Item at index 0 is: " listOfIntegers.get(0));

実際のアプリケーション内では、おそらく List に複数のレコードまたはビジネス・オブジェクトを格納して、処理の一環としてそのコレクションをひと通り調べることになります。そのための一般的な方法は何だと思いますか?正解: コレクションを繰り返し処理します。それが可能な理由は、Listjava.lang.Iterable インターフェースを実装するためです。

Iterable

コレクションが java.lang.Iterable を実装する場合、それは「イテラブル・コレクション」と呼ばれます。コレクションの一方の端から項目がなくなるまで、コレクションの項目を 1 つずつ調べていきます。

ループ」のセクションで、Iterable インターフェースを実装するコレクションを繰り返し処理するための特殊な構文について簡単に触れましたが、ここで改めて詳細を説明します。以下に、抽象的なコードを示します。

for (objectType varName : collectionReference) {
  // Start using objectType (via varName) right away...
}

より実際的なコードになると、以下の例のようになります。

List<Integer> listOfIntegers = obtainSomehow();
Logger l = Logger.getLogger("Test");
for (Integer i : listOfIntegers) {
  l.info("Integer value is : " + i);
}

以下のコード・スニペットは上記のスニペットよりも長くなっていますが、処理内容は同じです。

List<Integer> listOfIntegers = obtainSomehow();
Logger l = Logger.getLogger("Test");
for (int aa = 0; aa < listOfIntegers.size(); aa++) {
  Integer I = listOfIntegers.get(aa);
  l.info("Integer value is : " + i);
}

省略構文を使用した最初のスニペットでは、初期化するインデックス変数 (この例の場合は aa) もなければ、Listget() メソッドも呼び出していません。

List は、Iterable を実装する java.util.Collection を継承するため、どの List でも省略構文を使って繰り返し処理できます。

Set

Set は、本質的にすべて一意の要素を格納するコレクション構成体です (つまり、コレクション内に重複する要素はありません)。List には同じオブジェクトをおそらく数百回でも含めることができますが、Set にはどのインスタンスも 1 回しか含めることができません。Java の Set コレクションに格納できるのはオブジェクトに限られ、動作に関して厳格な規則が定義されます。

Set はインターフェースなので、直接インスタンス化することはできません。私が気に入っている実装のうち 1 つは、HashSet です。この実装は使いやすく、List に似ています。

Set では、例えば以下の処理を行うことができます。

  • Set に何らかの要素を格納する
  • Set に現在の自身の大きさを尋ねる
  • Set から何らかの要素を取得する

Set を差別化する特性は、そこに含まれる要素の間で一意性を保証することですが、要素の順序についての決まりはありません。一例として、以下のコードを見てください。

Set<Integer> setOfIntegers = new HashSet<Integer>();
setOfIntegers.add(Integer.valueOf(10));
setOfIntegers.add(Integer.valueOf(11));
setOfIntegers.add(Integer.valueOf(10));
for (Integer i : setOfIntegers) {
  l.info("Integer value is: " + i);
}

Set 内に 3 つの要素があると期待するかもしれませんが、値 10 が含まれる Integer オブジェクトは 1 回しか追加されないため、Set に格納される要素は 2 つだけです。

以下のように Set を繰り返し処理する際は、この動作を念頭に置いてください。

Set<Integer> setOfIntegers = new HashSet();
setOfIntegers.add(Integer.valueOf(10));
setOfIntegers.add(Integer.valueOf(20));
setOfIntegers.add(Integer.valueOf(30));
setOfIntegers.add(Integer.valueOf(40));
setOfIntegers.add(Integer.valueOf(50));
Logger l = Logger.getLogger("Test");
for (Integer i : setOfIntegers) {
  l.info("Integer value is : " + i);
}

Set は一意性を保証する一方、順序は保証しないので、出力されるオブジェクトの順序は、オブジェクトを追加した順序とは異なる可能性があります。結果を確認するには、上記のコードを Person クラスの main() メソッド内に貼り付けてからクラスを実行してください。

Map

Map は、あるオブジェクト (キー) を別のオブジェクト (値) に関連付けるために使用できる便利なコレクション構成体です。ご想像のとおり、Map のキーは一意でなければなりません。キーを使用して、そのキーに関連付けられた値を取得します。Java の Map コレクションに格納できるのはオブジェクトに限られ、動作に関して厳格な規則が定義されます。

Map はインターフェースなので、直接インスタンス化することはできません。私が気に入っている実装の 1 つは HashMap です。

Map で実行できる処理には以下が含まれます。

  • Map に何らかの要素を格納する
  • Map から何らかの要素を取得する
  • Map のキーが格納された Set を取得する (繰り返し処理するため)。

Map に要素を格納するには、その要素のキーを表すオブジェクトと、そのキーの値を表すオブジェクトが必要です。

public Map<String, Integer> createMapOfIntegers() {
  Map<String, Integer> mapOfIntegers = new HashMap<>();
  mapOfIntegers.put("1", Integer.valueOf(1));
  mapOfIntegers.put("2", Integer.valueOf(2));
  mapOfIntegers.put("3", Integer.valueOf(3));
  //...
  mapOfIntegers.put("168", Integer.valueOf(168));
return mapOfIntegers;
}

上記の例で、Map に格納される Integer のキーは、図らずもその値の String 表現となっている String です。特定の Integer 値を取得するには、その値の String 表現が必要になります。

mapOfIntegers = createMapOfIntegers();
Integer oneHundred68 = mapOfIntegers.get("168");

Map で Set を使用する

ときには、Map の参照を使用していて、そこに含まれるすべての要素を調べなければならないことがあります。その場合には、Map のキーが格納された Set が必要になります。

Set<String> keys = mapOfIntegers.keySet();
Logger l = Logger.getLogger("Test");
for (String key : keys) {
  Integer  value = mapOfIntegers.get(key);
  l.info("Value keyed by '" + key + "' is '" + value + "'");
}

Map から取得する IntegertoString() メソッドは、Logger 呼び出しの中で使用されると自動的に呼び出されることに注意してください。Map にはキーが設定されていて、各キーが一意であることから、Map は自身に含まれるキーを格納した Set を返します。Set を差別化する特性は (順序ではなく) 一意性です (おそらくこの点が、keyList() メソッドがなぜないのかを説明するでしょう)。

Java コードをアーカイブする

Java アプリケーションを作成する方法がわかってきたところで、皆さんは他の開発者が利用できるようにコードをパッケージ化する方法、あるいは他の開発者のコードを自分のアプリケーションにインポートする方法について考え始めているかもしれません。このセクションで、その方法を説明します。

JAR

JDK には、Java アーカイブ (Java Archive) を表す JAR というツールが同梱されています。このツールを使用して、JAR ファイルを作成します。コードを JAR ファイルにパッケージ化した後は、他の開発者がその JAR ファイルを自分のプロジェクトに取り込んで、別の開発者が作成したコードを使用するようにプロジェクトを構成できます。

Eclipse 内では JAR ファイルを簡単に作成できます。ワークスペース内で com.makotojava.intro パッケージを右クリックし、「File (ファイル)」 > 「Export (エクスポート)」をクリックします。これにより、図 10 に示すダイアログ・ボックスが表示されます。そのダイアログ・ボックスで「Jave」 > 「Jave file (Java ファイル)」に順に選択し、「Next (次へ)」をクリックします。

図 10. 「Export (エクスポート)」ダイアログ・ボックス
Eclipse の「Export (エクスポート)」ダイアログ・ボックスのスクリーンショット
Eclipse の「Export (エクスポート)」ダイアログ・ボックスのスクリーンショット

次のダイアログ・ボックスが表示されたら、JAR ファイルを保存する場所を参照し、ファイルに任意の名前を付けます。ファイル拡張子には、デフォルトの .jar を使用することをお勧めします。「Finish (完了)」をクリックします。

保存先として選択した場所に、JAR ファイルが生成されているはずです。ファイルに含まれるクラスをコードの中で使用できるようにするには、生成された JAR を Eclipse 内のビルド・パスに追加します。その方法は、次に説明するように簡単です。

サード・パーティー製アプリケーションを使用する

JDK は包括的なパッケージですが、優れた Java コードを作成する際に必要なものが見つからない場合もあります。Java アプリケーションを作成する作業に慣れていくうちに、作成するコードのサポートをサード・パーティー製アプリケーションに頼る場合が増えていくことが考えられます。Java オープンソース・コミュニティーでは、このようなギャップを埋めるのに役立つ多数のライブラリーを提供しています。

例えば、JDK に代わる Java コア・クラスの操作用ライブラリーとして Apache Commons Lang を使用するとします。Commons Lang によって提供されるクラスは、配列の処理、乱数の生成、ストリング処理に利用できます。

JAR ファイルに格納された形の Commons Lang をダウンロード済みであるという前提で、このファイルに含まれるクラスを使用するための最初のステップは、プロジェクト内に lib ディレクトリーを作成して、そこに JAR ファイルをドロップすることです。

  1. Eclipse の「Project Explorer (プロジェクト・エクスプローラー)」ビューで、「Intro」ルート・フォルダーを右クリックします。
  2. 「New (新規)」 > 「Folder (フォルダー)」をクリックし、新規フォルダーに lib という名前を付けます。
  3. 「Finish (完了)」をクリックします。

新しく作成されたフォルダーが、src と同じレベルに表示されます。Commons Lang JAR ファイルをこの新規 lib ディレクトリーにコピーします。この例でのファイル名は、commons-lang3-3.4.jar です (通常、JAR ファイルには、バージョン番号 (この例では 3.4) を含めた名前が指定されます)。

あとは、commons-lang3-3.4.jar ファイルに含まれるクラスをプロジェクトに含めるように Eclipse に指示すれば、クラスを使用できるようになります。

  1. 「Package Explorer (パッケージ・エクスプローラー)」で、lib フォルダーを選択して右クリックし、「Refresh (更新)」を選択します。
  2. lib フォルダー内に JAR があることを確認します。 更新後の lib フォルダーを示す画面のスクリーンショット
    更新後の lib フォルダーを示す画面のスクリーンショット
  3. commons-lang3-3.4 を右クリックし、「Build Path (ビルド・パス)」 > 「Add to Build Path (ビルド・パスに追加)」を選択します。

Eclipse によって JAR ファイル内のコード (クラス・ファイル) が処理された後、それらのクラスを Java コードから参照 (インポート) できるようになります。「Package Explorer (パッケージ・エクスプローラー)」に、「Referenced Libraries」という名前の新しいフォルダーが表示されていることに注目してください。ここに、commons-lang3-3.4.jar ファイルが保管されています。

優れた Java コードを作成する方法

ここまでのところで、基本的な Java プログラムを作成するには十分な Java の構文についての知識を身に付けました。つまり、このチュートリアルの前半を完了したということです。この最後のセクションでは、簡潔で保守しやすい Java コードを作成するための参考となるベスト・プラクティスをいくつか説明します。

クラスのサイズは小さくしておくこと

このチュートリアルでは、いくつかのクラスを作成しました。属性の数が (実際の Java クラスでの標準的な数からすると) わずかであるとしても、getter/setter のペアを生成した後の Person クラスのコードは 150 行になっています。これだけのサイズであっても、Person はまだ小さいクラスです。50 個あるいは 100 個のメソッドがあり、ソース・コードが数千行を超えるクラスを目にするのは珍しいことではありません。クラスによっては、やむを得ずそれだけの大きさになることもありますが、通常はリファクタリングが必要なはずです。リファクタリングとは、既存のコードの結果が変わることなく、その設計を変更することです。リファクタリングの際は、以下に説明するベスト・プラクティスに従うことをお勧めします。

通常、クラスはアプリケーションに含まれる概念エンティティーを表します。したがって、クラスのサイズはそのエンティティーに必要とされる機能だけを反映すべきなので、クラスが実行する機能は少数に絞り込んで、それらの機能を効果的に処理できるようにしてください。

必要なメソッドだけに限定してください。基本的に同じ処理を行う一方、それぞれに異なるパラメーターを取る複数のヘルパー・メソッドが必要な場合は、それらのヘルパー・メソッドをクラスに含めるのは仕方のない選択です。ただし、メソッドは必要なものだけに限り、不要なメソッドは排除するようにしてください。

メソッドの名前は慎重に決めること

メソッド名に関して言えば、その目的が明らかなメソッド名パターンが優れたコーディング・パターンとなります。このパターンを理解するには、単純な例を見るのが最も簡単です。以下のメソッド名のうち、一目でメソッドの目的が理解できるものはどちらですか?

  • a()
  • computeInterest()

答えは明らかなはずですが、どういうわけか、プログラマーはメソッドに (ついでに言うと、変数にも) 短く省略した名前を付ける傾向があります。もちろん、とんでもなく長い名前にすると不便ですが、メソッドの処理内容は、途方もなく長い名前でなくても伝えることができます。一連のコードを作成してから半年も経つと、例えばメソッドの名前が compInt() の場合、そのメソッドでどのような処理を行うつもりだったのか思い出せないでしょう。けれどもそれが computeInterest() という名前であれば、利息を計算するメソッドであることは明らかです。

メソッドのサイズは小さくしておくこと

メソッドのサイズは、サイズの小さいクラスと同様の理由で小さくすることが推奨されます。私が常々従うように努めている手法の 1 つは、画面を見たときに 1 ページに収まるようにメソッドのサイズを抑えることです。この手法に従うことで、アプリケーションのクラスが保守しやすくなります。

メソッドが 1 ページを超えるサイズになった場合、私はコードをリファクタリングします。Eclipse には素晴らしいリファクタリング・ツール一式が揃っています。通常、長いメソッドは機能のサブグループに分割できます。そのような機能のサブグループを別のメソッドに移し (そして適切な名前を付けて)、必要に応じてパラメーターを渡します。

各メソッドで処理するジョブは 1 つに制限してください。私の経験から、メソッドで 1 つのジョブだけを上手に処理するには、通常は約 30 行ほどのコードしか必要になりません。

新米プログラマーが学ばなければならない最も重要なスキルは、リファクタリングのスキルと、テスト・ファーストのコードを作成するスキルの 2 つです。誰もがこの両方に長けているとしたら、業界は革命的に変化することでしょう。この 2 つのスキルを上達させていけば、多くの同僚よりもより簡潔なコードとより機能的なアプリケーションを作成できるようになります。

コメントを活用すること

どうか、コメントを活用してください。そうすれば、そのコードを引き継ぐ次の開発者から感謝されるはずです (あるいは、6 カ月後の自分自身から感謝されることにもなります)。聞いたことがあるかもしれませんが、「上手く作成されたコードは自己を文書化している (Well-written code is self-documenting)」という古い格言があるのに、なぜコメントが必要なのでしょうか?私は以下の 2 つの理由から、この格言は当てはまらないと思います。

  • ほとんどのコードは上手く作成されていません。
  • 努力したとしても、思うほどにコードを上手く作成することはできないでしょう。

ですから、コードにコメントを付けてください。以上です。

一貫したスタイルに従うこと

コーディング・スタイルは個人の好みの問題ですが、中括弧には以下の標準的な Java 構文を使用することをお勧めします。

public static void main(String[] args) {
}

以下のようなスタイルは使用しないでください。

public static void main(String[] args)
{
}

あるいは、以下のスタイルも禁物です。

public static void main(String[] args)
  {
  }

その理由は、Java の標準的なスタイルに従うべきだからです。皆さんが目にするほとんどのコード (例えば、自分で作成したのではなく、保守するようお金を払って頼まれたコード) も、この標準的なスタイルで作成されているはずです。Eclipse ではコードのスタイルとフォーマットを自由に定義できるようになっています。けれども、Java の初心者である皆さんには、自分のスタイルというものはまだないと思うので、最初から Java の標準を採用することを提案します。

組み込みロギング機能を使用すること

Java 1.4 で組み込みロギング機能が導入される前は、プログラムの処理内容を調べる標準的な方法は、以下のようなシステム呼び出しを行うことでした。

public void someMethod() {
  // Do some stuff...
  // Now tell all about it
  System.out.println("Telling you all about it:");
  // Etc...
}

上記のシステム呼び出しを使うより、Java 言語の組み込みロギング機能 (「初めての Java クラスを作成する」のセクションを参照) のほうが優れた手段です。私がコードの中で System.out.println() を使用することは決してありません。皆さんにも、使用しないことをお勧めします。別の手段としては、一般的に使用されている log4j ライブラリーという Apache 傘下のプロジェクトもあります。

第 1 回のまとめ

このチュートリアルでは、オブジェクト指向プログラミングについて説明し、有用なオブジェクトを作成するために使用できる Java の構文を見ていきました。また、開発環境を制御するのに役立つ IDE についても紹介しました。このチュートリアルを通して、異なる入力に応じて異なる動作を行うなどといった数々の処理をも実行できる Java オブジェクトを作成して実行する方法、作成したアプリケーションを他の開発者がそのプログラム内で使用できるように JAR を生成する方法を学びました。そして、基本的な Java プログラミングのベスト・プラクティスについての知識も身に付けてもらえたはずです。

次回の予告

このチュートリアルの第 2 回では、より高度な Java プログラミングの構成体をいくつか取り上げて説明しますが、全体を通して入門範囲の説明にとどめます。次回のチュートリアルで取り上げる Java プログラミングのトピックは以下のとおりです。

  • 例外処理
  • 継承と抽象化
  • インターフェース
  • ネストされたクラス
  • 正規表現
  • ジェネリック
  • 列挙型
  • 入出力
  • シリアライズ

Java プログラミング入門、第 2 回: 実際のアプリケーションに対応するための構成体」を読んでください。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology
ArticleID=1049932
ArticleTitle=Java プログラミング入門、第 1 回: Java 言語の基本
publish-date=09212017