はじめに
この記事を読めば、Javaでimplements
キーワードを使いこなすことができるようになります。
Javaでよく使われるimplements
とは何か、どのような場面で必要なのか、そして実際にどのように使えばいいのか。
この記事ではこれらを10のステップと詳細なサンプルコードを通して徹底的に解説します。
●Javaとimplementsの基本概念
○Javaとは
Javaは、1995年にSun Microsystems(現在はOracle Corporationに買収されています)によって開発されたプログラミング言語です。
一度書いたプログラムが、さまざまなプラットフォームで動作するという特徴があります。
これは、「Write Once, Run Anywhere」とも表現されます。Javaはウェブアプリケーション、スマートフォンアプリ、組み込みシステムなど、多様な分野で使用されています。
○implementsキーワードとは
Javaでは、implements
キーワードは、あるクラスが特定のインターフェースを実装する際に使用されます。
インターフェースとは、抽象メソッド(具体的な処理内容のないメソッド)の集合であり、これを実装(implements
)するクラスは、そのインターフェースで定義された抽象メソッドを全て具体的に処理する必要があります。
インターフェースの役割は、実装すべきメソッドの「形」を定義することです。
そのため、implements
キーワードを使ってインターフェースを実装すると、その「形」に従ってクラスにメソッドを追加する必要があります。
例えば、Runnable
インターフェースを実装するには、その中で定義されているrun
メソッドを必ずオーバーライド(新しく定義し直すこと)しなければなりません。
このようにして、Javaではimplements
キーワードを使用することで、特定の機能を持つクラスを簡単に作成することができます。
●implementsの詳細な使い方
ここでimplements
の詳細な使い方について解説していきます。
Javaでよく使われるこのキーワード、実際にはどう使うのでしょうか。
具体的なサンプルコードを通じて、その全貌を解明していきましょう。
○サンプルコード1:単純なインターフェースの実装
最初に、一番基本的な形でインターフェースを実装してみましょう。
このサンプルでは、Animal
インターフェースを作成し、Dog
クラスでそれを実装します。
このコードでは、Animal
インターフェースにbark
というメソッドを定義しました。
そして、Dog
クラスでこのAnimal
インターフェースを実装(implements
)しています。
bark
メソッドは、犬が「ワンワン」と鳴く動作をコンソールに出力します。
このコードを実行すると、犬が「ワンワン」と鳴く動作がコンソールに出力されます。
○サンプルコード2:複数のインターフェースを実装
Javaでは一つのクラスが複数のインターフェースを実装することも可能です。
このサンプルでは、Animal
という別のインターフェース、Pet
を作成し、Dog
クラスで両方を実装してみます。
このコードでは、Animal
インターフェースとPet
インターフェースの2つをDog
クラスで実装しています。
それぞれのインターフェースで定義されたbark
メソッドとbeFriendly
メソッドをオーバーライドしています。
このコードを実行すると、犬が「ワンワン」と鳴く動作と、犬が「しっぽを振る」という動作がコンソールに出力されます。
○サンプルコード3:インターフェースのメソッドをオーバーライド
Javaでインターフェースを実装する際、そのメソッドは必ずオーバーライドする必要があります。
ここではAnimal
インターフェースを拡張し、run
という新たなメソッドを追加します。
その後で、Dog
クラスでこの新しいメソッドも含めてオーバーライドしてみましょう。
このコードでは、Animal
インターフェースに新たにrun
メソッドが追加されています。
このrun
メソッドもDog
クラスでしっかりとオーバーライドしている点に注意してください。
この場合のrun
メソッドは犬が「走る」という動作をコンソールに出力します。
このコードを実行すると、Dog
クラスのオブジェクトが「ワンワン」と吠えるとともに、「走る」という動作をすることがコンソールで確認できます。
○サンプルコード4:抽象クラスとの違い
Javaには抽象クラスという概念もありますが、インターフェースとは何が違うのでしょうか。
主な違いは「状態を持つことができるかどうか」です。
抽象クラスは状態(フィールド変数)を持つことができますが、インターフェースはメソッドの定義のみが可能です。
このコードでは、AnimalAbstract
という抽象クラスを作成しています。
この抽象クラスにはname
というフィールド変数が存在します。
その後で、Dog
クラスがこの抽象クラスを継承(extends
)しています。
このコードを実行すると、Dog
クラスのオブジェクトmyDog
が「ポチがワンワンと吠える」という動作とともに、その名前も出力される点に注目してください。
○サンプルコード5:デフォルトメソッドの利用
Java 8以降、インターフェースにもメソッドの実装が可能になりました。これをデフォルトメソッドと呼びます。
デフォルトメソッドは、新しくメソッドがインターフェースに追加されても、既存のクラスに影響を与えないように設計されています。
早速、サンプルコードを通じてデフォルトメソッドの使い方を解説します。
このJavaコードで目を引くのは、Animal
インターフェース内にrun
メソッドがデフォルトメソッドとして定義されている点です。
デフォルトメソッドは、default
キーワードを使用してメソッドに実装を持たせられます。
このデフォルトメソッドを使用すると、それを実装したDog
クラスでも自動的にrun
メソッドが利用できるようになります。
実行結果としては、「ワンワン」と「動物が走る」がコンソールに出力されます。
●implementsの応用例
Javaのimplements
キーワードとインターフェースは、非常に多面的で柔軟な設計が可能です。
ここでは、実際によく使われる応用例をサンプルコードと共に詳しく解説します。
○サンプルコード6:コレクションフレームワークでの利用例
Javaのコレクションフレームワークでよく使われるインターフェースがあります。特に、List
やSet
、Map
などが代表的です。
このようなインターフェースは、具象クラス(例:ArrayList
, HashSet
)で実装されています。
このサンプルコードは、List
インターフェースとその具象クラスであるArrayList
を使用しています。
実行すると、Apple、Banana、Cherryが順番に出力されます。
○サンプルコード7:イベントリスナーとしての使用例
Javaにおいてイベント処理を行う際にも、implements
が使われます。
イベントリスナーと呼ばれるインターフェースがあり、これを実装することで特定のイベントに対する処理を記述することができます。
このサンプルコードでは、JavaのSwingライブラリを使い、ボタンのクリックイベントを処理しています。
ActionListener
インターフェースをimplements
して、そのactionPerformed
メソッドをオーバーライドしています。
ボタンをクリックすると、ボタンのラベルが「Clicked!」に変わります。
○サンプルコード8:ストラテジーパターンにおける利用例
デザインパターンの一つであるストラテジーパターンは、具体的な処理をカプセル化して、それを簡単に切り替えられるようにする方法です。
このストラテジーパターンも、implements
を用いてJavaで実装することが一般的です。
ストラテジーパターンでは、アルゴリズム(処理)をインターフェースとして定義します。
その後、このインターフェースをimplements
する形で具体的なアルゴリズムを表現するクラスを作成します。
このサンプルコードでは、PaymentStrategy
という名前のインターフェースを作成しています。
このインターフェースにはpay
というメソッドが定義されています。
CreditCardPayment
とCashPayment
は、このインターフェースをimplements
しています。
それぞれのクラスでpay
メソッドが異なる処理(支払い方法)を持っています。
このプログラムを実行すると、出力としては「クレジットカードで3000円支払いました。」と「現金で5000円支払いました。」がそれぞれ表示されます。
○サンプルコード9:ユーザー定義インターフェースの作成
Javaでは、独自にインターフェースを定義して使用することが可能です。
この機能を利用して、特定のビジネスロジックや規則に合わせた処理を行うことができます。
このサンプルコードでは、Greeting
という自作のインターフェースを定義しています。
そして、そのインターフェースをimplements
して、EnglishGreeting
とJapaneseGreeting
という2つのクラスを作成しています。
プログラムを実行すると、「Hello, John!」と「こんにちは、太郎さん!」がそれぞれ出力されます。
○サンプルコード10:implementsとジェネリクス
ジェネリクスは、型の安全性を高めるためにJavaに導入されました。
これを使うと、コンパイル時に型チェックが行われ、実行時の型キャストの必要が減少します。
特に、ジェネリクスとimplements
を組み合わせることで、非常に柔軟で再利用可能なコードを作成することができます。
ジェネリクスを用いたインターフェースの実装例を紹介します。
このサンプルコードでは、Storage
という名前のジェネリクスを持つインターフェースを定義しています。
このインターフェースは、add
とget
という2つのメソッドを持っています。
StringStorage
クラスは、このインターフェースをimplements
し、具体的にString
型のデータを保存する動作を持っています。
上記の実行クラスGenericsExample
を実行すると、「Java」という文字列が出力されます。
これは、StringStorage
クラスのadd
メソッドでデータを保存し、get
メソッドで取得しているからです。
ジェネリクスを使用すると、同じロジックをさまざまな型のデータに適用できるため、コードの再利用性が高まります。
また、implements
を使用してジェネリクスを持つインターフェースを実装することで、具体的な型のデータを扱うクラスを柔軟に作成することができます。
●注意点と対処法
Javaでimplements
キーワードを用いてインターフェースを実装する際には、多くの注意点と対処法が存在します。
これらに気を付けながらプログラムを作成することで、より効率的でバグの少ないコードが書けます。
○メソッドのシグネチャに注意
Javaのインターフェースを実装するには、インターフェースで定義されているメソッドのシグネチャ(メソッド名、引数の型、戻り値の型)を正確にオーバーライドする必要があります。
シグネチャが一致しないと、コンパイルエラーが発生します。
上記のコード例では、WrongDisplay
クラスがDisplay
インターフェースを正確に実装していないため、コンパイルエラーが出ます。
これを避けるためには、show
メソッドの引数をString
型に修正する必要があります。
このようにメソッドのシグネチャが一致しているかどうかは、コンパイル時に検証されるため、開発者はこの点を注意深く確認する必要があります。
○アクセス修飾子の注意点
インターフェースで定義されるメソッドは、暗黙的にpublic
アクセス修飾子が設定されています。
そのため、インターフェースを実装するクラスでも、そのメソッドはpublic
でなければなりません。
この例のDog
クラスは、speak
メソッドにアクセス修飾子が設定されていないため、コンパイルエラーが発生します。
この問題を解決するには、speak
メソッドにpublic
アクセス修飾子を追加する必要があります。
○ダイヤモンド問題とその対処法
Javaでは、一つのクラスが複数のインターフェースを実装できるので、ダイヤモンド問題が発生する可能性があります。
具体的には、複数のインターフェースが同じメソッドを持っている場合に問題が発生します。
上記のCar
クラスでは、Engine
とVehicle
という2つのインターフェースを実装していますが、両方のインターフェースでstart
メソッドが定義されているため、Car
クラス内で一度このメソッドを実装すれば、両方のstart
メソッドに対する実装とみなされます。
●カスタマイズ方法
Javaでimplements
を用いたインターフェースの実装を理解し、基本的な使い方や注意点を把握したら、次はさまざまなカスタマイズ方法について学びましょう。
○インターフェースの拡張
Javaでは、既存のインターフェースを拡張して新しいインターフェースを定義することができます。
この技術は、継承の一形態とも言えます。
このコード例では、Shape
インターフェースにdraw
メソッドがあり、これを拡張したColoredShape
インターフェースがsetColor
メソッドを追加しています。
Square
クラスはColoredShape
を実装しているため、draw
メソッドとsetColor
メソッドの両方をオーバーライドしています。
このコードを実行する際に、Square
オブジェクトのsetColor
とdraw
メソッドを呼び出すと、「赤の四角形を描画します。」のように出力されます。
○デフォルトメソッドのオーバーライド
Java 8以降、インターフェース内でメソッドに実装(デフォルトメソッド)を提供することができます。
しかし、これをそのまま使うのではなく、必要に応じてオーバーライドすることもできます。
このコード例では、Greeter
インターフェースがデフォルトメソッドgreet
を持っています。
JapaneseGreeter
クラスはこのメソッドをオーバーライドして、日本語で挨拶を出力します。
このコードを実行すると、JapaneseGreeter
オブジェクトのgreet
メソッドを呼び出すと「こんにちは、世界!」と出力されます。
○自作インターフェースでの応用
Javaで自分自身が定義したインターフェースを使って、特定の機能や規約を強制することも可能です。
このコードでは、Calculator
という自作インターフェースにadd
メソッドを定義し、それをSimpleCalculator
クラスで実装しています。
このコードを実行すると、SimpleCalculator
オブジェクトのadd
メソッドを用いて数値を加算できます。
例えば、add(3, 4)
とすると、7が返されます。
まとめ
Javaでのimplements
キーワードの使い方について、基本的なコンセプトから詳細な使い方、注意点、そして応用テクニックまで幅広く解説してきました。
この記事を通じて、Javaプログラミングにおけるインターフェースの重要性や活用方法についての理解が深まったことと思います。
Javaのimplements
は、インターフェースを実装するための非常に強力な機能であり、設計段階での柔軟性や拡張性を確保するための鍵となる要素です。
特に、インターフェースを活用することで、コードの再利用性が高まり、メンテナンスも効率的に行えるようになります。
継続的な学習と実践を通じて、更なるスキル向上を目指しましょう。