はじめに
なぜ、タイトルのような状況に陥ったのか...
Springフレームワークの勉強をしようかなと思い、公式ドキュメント「spring公式webページ」をパラパラと覗いていたところ「依存性の注入(DI)」という概念(?)があることを知りました。
そこで参考にさせていただいたのが、こちらのサイト「プログラミングTIPS!」
依存性の注入について、とても分かりやすく書かれているのですが、私のJavaへの理解が浅かったために沼にはまってしまいました。
この記事の対象者
- 初心者の沼を眺めたい方
- 私と同じ初学者の方
- 温かい目で見守っていただける方
どこではまったのか
参考にした「プログラミングTIPS!」の具体例の部分、DIを行う場合の単体テストの例で
-
Service
クラス
class Service {
DayOfWeek getWeek() {
return LocalDate.now().getDayOfWeek();
}
}
-
Client
クラス
class Client {
private final Service service;
// ★ポイント!
Client(Service service) {
this.service = service;
}
String getMessage() {
final var week = service.getWeek();
return switch (week) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "今日は平日!";
case SATURDAY, SUNDAY -> "今日は休日!";
};
}
}
- 単体テスト例 ☆
final var dummy = new Service() {
@Override
DayOfWeek getWeek() {
// 月曜日
return DayOfWeek.MONDAY;
}
};
final var client = new Client(dummy);
final var actual = client.getMessage();
System.out.println("今日は平日!".equals(actual) ? "OK" : "NG");
// 結果
// ↓
// OK
そう、この単体テスト例のところで沼りました
なぜ、はまったのか
答えはシンプルです。 曖昧に覚えていたのが仇となってOverride
とOverload
がごちゃごちゃになってたんですね。
なので@Override
をみて「あ~あれね!なるほど(Overload
のことを思い浮かべながら)」となり、その次の行で引数は変わってないのに再度書き直されるgetWeek
メソッド君に「…。(なぜ君がそこで再度書かれているのだ)」となったわけです。
- Override
とは from ChatGPT
"Override"(オーバーライド)はプログラミングにおける用語で、特にオブジェクト指向プログラミングにおいて重要な概念です。オーバーライドは、サブクラスがスーパークラスから継承したメソッドを再定義するプロセスを指します。サブクラスはスーパークラスのメソッドと同じ名前、戻り値の型、引数リストを持つメソッドを定義することで、そのメソッドをオーバーライドすることができます。
オーバーライドの目的は、継承されたクラスの動作をサブクラスのニーズに合わせて変更することです。これにより、ポリモーフィズムの実現が可能になります。ポリモーフィズムとは、異なるクラスのオブジェクトが同じインターフェースまたはメソッド呼び出しに対して異なる動作をする能力のことを指します。オーバーライドを使うことで、異なるタイプのオブジェクトが共通のインターフェースを持ちながら、それぞれ独自の実装を提供することができます。
※以下は私がこんなかんじとおもって書いたものです
class Animal{
public void makeSound(){
System.out.println("");
}
}
class Dog extends Animal{
@Override
public void makeSound(){
System.out.println("Bow");
}
}
- Overload
とは from ChatGPT
"Overload"(オーバーロード)もプログラミングにおける重要な概念で、特にオブジェクト指向プログラミングにおいてよく使用されます。オーバーロードは、同じ名前のメソッドや関数が異なる引数のリストを持つことを可能にする機能です。これにより、同一のメソッド名で異なるタイプや数のパラメータを取る複数のバージョンを定義することができます。
オーバーロードの主な目的は、関数やメソッドの柔軟性を高めることです。プログラマは、異なるシナリオで異なる入力を処理するために、同じ関数名を使いながら複数の実装を提供することができます。コンパイラまたはインタプリタは、呼び出された関数やメソッドのシグネチャ(名前、引数のタイプと数)に基づいて、適切なバージョンを実行時に選択します。
オーバーロードの例としては、add関数があります。add関数をオーバーロードして、2つの整数の和を計算するバージョン、2つの浮動小数点数の和を計算するバージョン、2つの文字列を連結するバージョンを提供することができます。それぞれのadd関数は、引数のタイプや数が異なりますが、同じ関数名を共有しています。
オーバーロードは、プログラムの表現力を高める有効な手段であり、関数やメソッドの使用をより直感的にすることができます。しかし、オーバーロードされたメソッドの多用は、コードの理解を難しくすることもあるため、適切なバランスで使用することが推奨されます。
※以下は私がこんなかんじとおもって書いたものです
class Animal{
public void makeSound(){
System.out.println("");
}
public void makeSound(String voice){
System.out.println(voice);
}
public void makeSound(String voice, int count){
for(int i=0;i<count;i++){
System.out.println(voice);
}
}
}
ふたつの違いの覚え方 from ChatGPT
Override(オーバーライド)とOverload(オーバーロード)の違いを覚えるために、役立つ簡単な方法をいくつか紹介します。これらの概念は似ているように感じられますが、役割と使用の文脈が異なります。
オーバーライド (Override)
- 「上書き」と覚える。サブクラスがスーパークラスのメソッドを上書きします。
- 覚え方:「親の遺産を自分のものにする」。親(スーパークラス)が残したメソッド(遺産)を子(サブクラス)が自分のものとして変更(上書き)します。
オーバーロード (Overload)
- **「過負荷」**と覚える。同じ名前のメソッドが複数のパラメータリストを持ち、機能を過負荷させます。
- 覚え方:「電話に複数の番号」。一つの電話(メソッド名)に対して、複数の番号(パラメータのリスト)を割り当てることができます。異なる番号(パラメータ)で呼び出すと、異なる処理が行われます。
共通の覚え方
- オーバーライドは「継承」に関連しています。サブクラスがスーパークラスのメソッドを継承し、その機能を自分のものにして変更します。
- オーバーロードは「多様性」に関連しています。同一のメソッド名が異なる入力パラメータを受け取ることで、さまざまな形で機能することができます。
このように、オーバーライドとオーバーロードは、それぞれ「継承と上書き」、「多様性と過負荷」をキーワードにして覚えると良いでしょう。これにより、これらの概念が混同しにくくなり、プログラミングにおける彼らの役割をより明確に理解することができます。
覚えやすっ!ChatGPT君の賢さに唖然としています。
どうやって解決したか
chatGPTにききました‼ ほんとに優秀なんだから。以下、初学者とchatGPT君の会話をご覧ください。
1投球目
私
java でインスタンス作成時にメソッドをオーバライドできますか
ChatGPT君
はい、Javaでインスタンスを作成する際に匿名クラスを使用してメソッドをオーバライドすることができます。これにより、その場でクラスの実装をカスタマイズし、特定のメソッドの動作を変更することが可能になります。以下に簡単な例を示します。
// 抽象クラスまたはインターフェイス
abstract class Animal {
abstract void makeSound();
}
public class Test {
public static void main(String[] args) {
// Animal クラスのインスタンスを作成し、makeSound メソッドをオーバライド
Animal myAnimal = new Animal() {
@Override
void makeSound() {
System.out.println("Meow");
}
};
myAnimal.makeSound(); // "Meow" が出力される
}
}
この例では、Animal
という抽象クラスがあり、makeSound
という抽象メソッドを持っています。main
メソッド内で、Animal
の匿名クラスのインスタンスを作成しています。このとき、makeSound
メソッドをオーバライドして、独自の実装(この例では "Meow" を出力)を提供しています。
このテクニックは、特にその場限りのカスタマイズが必要な場合や、特定のメソッドの振る舞いを変えたいが、新しいクラスを作るほどではない場合に便利です。
2投球目
私
匿名メソッド以外でもできますか?
ChatGPT君
Javaでは、インスタンス作成時に直接メソッドをオーバライドするために匿名クラスを利用する以外に、そのような直接のメソッドオーバライド(インスタンス毎に異なるメソッド実装を持つこと)をサポートする仕組みはありません。通常、メソッドのオーバライドは、あるクラスを継承するサブクラス内で行われます。
クラスが既に存在していて、そのクラスのインスタンスに対して特定のメソッドだけ異なる動作をさせたい場合、通常は以下のようなアプローチを取ります:
-
サブクラスを作成する: 既存のクラスを継承し、オーバライドしたいメソッドを持つサブクラスを定義します。この方法では、新しいクラスを明示的に定義する必要がありますが、オーバライドしたメソッドの挙動をカスタマイズできます。
-
デザインパターンを使用する: 特定のケースに応じて、ストラテジーパターンやデコレーターパターンなどのデザインパターンを使用して、オブジェクトの振る舞いを動的に変更することができます。これらのパターンは、オブジェクトの構成を通じて異なる振る舞いを提供する方法を提供します。
-
ラムダ式や関数インターフェースを使用する: Java 8以降、ラムダ式や関数インターフェースを使用して、特定の操作に対するコードブロックを簡潔に記述できます。これはメソッドオーバライドとは異なりますが、特定のインターフェースの実装を提供する際にコードを簡潔にする方法です。
以下は、サブクラスを作成してメソッドをオーバライドする例です:
class Animal {
void makeSound() {
System.out.println("Some sound");
}
}
class Cat extends Animal {
@Override
void makeSound() {
System.out.println("Meow");
}
}
public class Test {
public static void main(String[] args) {
Cat myCat = new Cat();
myCat.makeSound(); // "Meow" が出力される
}
}
この方法では、Cat
クラスが Animal
クラスを継承し、makeSound
メソッドをオーバライドしています。このアプローチは、再利用可能なカスタマイズを行いたい場合や、オブジェクト指向設計の原則に従ってシステムを設計したい場合に適しています。
3投球目(ここで、なんかおもってたんと違うなと思い始める)
私
同じ名前のメソッドで引数だけかわるのは何ていうんでしたっけ
ChatGPT君
同じ名前のメソッドで引数だけが異なる場合、それは「メソッドのオーバーロード(Overloading)」と呼ばれます。メソッドのオーバーロードでは、メソッド名は同じですが、引数の数や型が異なる複数のメソッドを同一クラス内に定義できます。これにより、異なる引数に応じて異なる処理を実行するメソッドを用意することができます。
オーバーロードの例:
class ExampleClass {
void display(int a) {
System.out.println("Integer: " + a);
}
void display(String a) {
System.out.println("String: " + a);
}
void display(int a, int b) {
System.out.println("Two integers: " + a + ", " + b);
}
}
この例では、display
メソッドは3回オーバーロードされています。1つ目は整数1つを引数に取り、2つ目は文字列を引数に取り、3つ目は整数を2つ引数に取ります。これらはすべて同じメソッド名(display
)を持っていますが、引数の型や数が異なるため、Javaはこれらを異なるメソッドとして扱います。これがメソッドのオーバーロードの基本的な概念です。
おわりに
名前は似ている二つの概念Override
とOverload
だけど機能はまったくもって違うことを覚えました。 これからは絶対に間違えないと思う...思う。
これからもめげずあきらめず勉強がんばります
応援していただけると嬉しいです
どの口が言ってるのって感じですが、みなさんも曖昧な知識にはご注意を。
おまけ
やさしいChatGPT君
私
オーバーライドとオーバーロード呼び方ややこしいね
ChatCPT君
はい、オーバーライド(Override)とオーバーロード(Overload)は似たような用語で、初学者にとっては混乱しやすいですね。しかし、これらはオブジェクト指向プログラミングにおける2つの重要な概念で、それぞれ異なる目的と動作を持っています。簡単に区別する方法をまとめると以下のようになります:
オーバーライド (Override)
- 目的: スーパークラスで定義されたメソッドの振る舞いをサブクラスで変更します。
- 条件: メソッド名、引数のリスト(型と数)、戻り値の型がスーパークラスのメソッドと完全に一致する必要があります。
- 使用場所: 継承関係にあるクラス間で使用されます。
オーバーロード (Overload)
- 目的: 同一クラス内で同じ名前のメソッドを複数定義し、引数の型や数によって異なる処理を実行できるようにします。
- 条件: メソッド名は同じである必要がありますが、引数のリスト(型や数)が異なる必要があります。戻り値の型はオーバーロードの判定には影響しません。
- 使用場所: 単一のクラス内で使用されます。
これらの違いを理解することは、Javaをはじめとする多くのオブジェクト指向言語で効果的にプログラミングする上で非常に重要です。慣れるまでは混乱するかもしれませんが、実際にコードを書いてみることで、徐々に違いを感じ取れるようになるでしょう。
参考リンク
おねがい
ほんとに初学者の備忘録なので内容に誤りがある可能性があります
見つけた際にはお手数ですがご連絡いただけると大変ありがたいです