Java はオブジェクト指向言語です。これは、オブジェクト指向パラダイムを使用して Java プログラムを作成する必要があることを意味します。そして、このパラダイムでは、プログラム内でオブジェクトとクラスを使用する必要があります。例を使用して、クラスとオブジェクトとは何か、および基本的な OOP 原則 (抽象化、継承、ポリモーフィズム、カプセル化) を実際に適用する方法を理解してみましょう。

オブジェクトとは何ですか?

私たちが住む世界は物体で構成されています。周りを見回すと、私たちは家、木、車、家具、食器、コンピューターに囲まれていることがわかります。これらはすべてオブジェクトであり、それぞれが一連の特定の特性、動作、目的を持っています。私たちはオブジェクトに慣れており、常にそれらを非常に特殊な目的に使用します。たとえば、仕事に行く必要がある場合、私たちは車を使います。食べたいときは食器を使います。休みたいときは、快適なソファを見つけてください。人間は、日常生活の問題を解決するためにオブジェクトの観点から考えることに慣れています。これが、プログラミングでオブジェクトが使用される理由の 1 つです。このアプローチはオブジェクト指向プログラミングと呼ばれます。例を挙げてみましょう。新しい携帯電話を開発し、量産を開始したいと考えていると想像してください。携帯電話の開発者なら、それが何のためにあるのかご存知でしょう。それがどのように機能するか、そしてその部品が何であるか(本体、マイク、スピーカー、ワイヤー、ボタンなど)。さらに、これらのパーツの接続方法はあなただけが知っています。しかし、あなたは携帯電話を個人で作るつもりはなく、従業員のチーム全体でこれをやっているのです。電話機の部品の接続方法を何度も説明する必要をなくし、すべての電話機が同じ方法で作られるようにするには、電話機の製造を開始する前に、電話機の構成を説明する図面を作成する必要があります。OOP では、このような説明、描画、ダイアグラム、またはテンプレートをクラスと呼びます。これは、プログラムの実行時にオブジェクトを作成する基礎を形成します。クラスは、フィールド、メソッド、コンストラクターで構成される共通テンプレートのような、特定のタイプのオブジェクトの記述です。オブジェクトはクラスのインスタンスです。

抽象化

次に、現実世界のオブジェクトからプログラム内のオブジェクトにどのように移動できるかを考えてみましょう。例として電話を使用します。この通信手段には100年以上の歴史があります。現代の電話は、19 世紀の前のものよりもはるかに複雑な装置です。電話を使用するとき、私たちはその構成や内部で発生するプロセスについて考えません。電話機の開発者が提供する機能、つまりボタンやタッチ スクリーンを使用して電話番号を入力し、電話をかけるだけです。最初の電話インターフェイスの 1 つは、電話をかけるために回転する必要があるクランクでした。もちろん、これはあまり便利ではありませんでした。しかし、その役割は完璧に果たしました。最新の携帯電話と最初の携帯電話を比較すると、19 世紀後半のデバイスと現代のスマートフォンにとって最も重要な機能がすぐにわかります。それは、電話をかける機能と電話を受ける機能です。実際、これこそが電話を電話たらしめているのであって、他のものではないのです。ここで、オブジェクトの最も重要な特性と情報を識別するという OOP の原則を適用しました。この原理は抽象化と呼ばれます。OOP では、抽象化は、現実世界のタスクの要素をプログラム内のオブジェクトとして表現する方法としても定義できます。抽象化は常に、オブジェクトの特定のプロパティの一般化と関連付けられるため、主なことは、当面のタスクのコンテキストにおいて重要でない情報から意味のある情報を分離することです。さらに、いくつかのレベルの抽象化が存在する可能性があります。させて' 抽象化の原理を携帯電話に適用してみます。まず、最初の電話から現在の電話まで、最も一般的なタイプの電話を特定します。たとえば、図 1 の図の形でそれらを表すことができます。 OOP の原則 - 2抽象化を使用すると、このオブジェクト階層内の一般的な情報、つまり一般的な抽象オブジェクト (電話機)、電話機の共通の特性 (たとえば、その製造年)、および共通のインターフェイス (すべての電話機が通話の受信と発信が可能) を識別できるようになります。Java では次のようになります。

public abstract class AbstractPhone {
    private int year;

    public AbstractPhone(int year) {
        this.year = year;
    }
    public abstract void call(int outgoingNumber);
    public abstract void ring(int incomingNumber);
}
プログラムでは、この抽象クラスを使用し、以下で説明する OOP の他の基本原則を適用して、新しいタイプの電話を作成できます。

カプセル化

抽象化により、すべてのオブジェクトに共通するものを特定します。しかし、どの種類の電話も独特であり、他の電話とは何らかの違いがあります。プログラムの中で、どのように境界線を引き、この個性を識別するのでしょうか? 誰も私たちの携帯電話を誤​​ってまたは故意に壊したり、あるモデルを別のモデルに変換しようとしたりできないようにするにはどうすればよいでしょうか? 現実の世界では、答えは明白です。すべての部品を電話ケースに入れる必要があります。結局のところ、そうしないと、代わりに携帯電話の内部部品をすべて残し、接続線を外部に残すと、好奇心旺盛な実験者が間違いなく携帯電話を「改良」したいと思うでしょう。このような改ざんを防ぐために、オブジェクトの設計と操作にはカプセル化の原理が使用されます。この原則は、オブジェクトの属性と動作が 1 つのクラス (オブジェクト) に結合されることを示しています。■ 内部実装はユーザーには隠されており、オブジェクトを操作するためのパブリック インタフェースが提供されます。プログラマの仕事は、オブジェクトの属性とメソッドのうちどの属性とメソッドをパブリック アクセスに使用できるようにするか、およびどの内部実装の詳細にアクセスできないようにするかを決定することです。

カプセル化とアクセス制御

電話機が製造されたときに、その背面に電話機に関する情報(製造年やメーカーのロゴ)が刻印されているとします。情報 (その状態) は、この特定のモデルに固有です。メーカーはこの情報が不変であることを確認したと言えます。刻印を削除しようと考える人はいないでしょう。Java の世界では、クラスはフィールドを使用して将来のオブジェクトの状態を記述し、その動作はメソッドを使用して記述します。オブジェクトの状態と動作へのアクセスは、フィールドとメソッドに適用される修飾子 (private、protected、public、default) を使用して制御されます。たとえば、製造年、メーカー名、メソッドの 1 つはクラスの内部実装の詳細であり、プログラム内の他のオブジェクトによって変更できないと判断しました。コードでは、

public class SomePhone {

    private int year;
    private String company;
    public SomePhone(int year, String company) {
        this.year = year;
        this.company = company;
    }
private void openConnection(){
    // findSwitch
    // openNewConnection...
}
public void call() {
    openConnection();
    System.out.println("Calling");
}

public void ring() {
    System.out.println("Ring-ring");
}

 }
private 修飾子を使用すると、このクラス内でのみクラスのフィールドとメソッドにアクセスできます。これは、プライベート メソッドを呼び出すことができないため、外部からプライベート フィールドにアクセスすることができないことを意味します。また、 openConnectionメソッドへのアクセスを制限すると、メソッドが他のオブジェクトによって使用されたり、他のオブジェクトの作業が中断されたりしないことが保証されるため、メソッドの内部実装を自由に変更できるようになります。オブジェクトを操作するには、public 修飾子を使用してcall メソッドRingメソッドを利用可能なままにしておきます。アクセスが完全に拒否されると役に立たなくなるため、オブジェクトを操作するためのパブリック メソッドの提供もカプセル化の一部です。

継承

電話の図をもう一度見てみましょう。これは、モデルがその分岐に沿って上位にあるモデルのすべての機能を持ち、独自の機能の一部を追加した階層であることがわかります。たとえば、スマートフォンは通信にセルラー ネットワークを使用し (携帯電話の特性を持ち)、ワイヤレスで持ち運び可能 (コードレス電話の特性を持ち)、通話の送受信が可能 (電話の特性を持ちます) です。ここで得られるのは、オブジェクトのプロパティの継承です。プログラミングにおいて、継承とは、既存のクラスを使用して新しいクラスを定義することを意味します。継承を使用してスマートフォン クラスを作成する例を考えてみましょう。すべてのコードレス電話機は充電式バッテリーで駆動されており、バッテリーには一定の寿命があります。したがって、このプロパティをコードレス電話クラスに追加します。

public abstract class CordlessPhone extends AbstractPhone {

    private int hour;

    public CordlessPhone (int year, int hour) {
        super(year);
        this.hour = hour;
    }
    }
携帯電話はコードレス電話のプロパティを継承しており、このクラスに callメソッドとRingメソッドを実装します。

public class CellPhone extends CordlessPhone {
    public CellPhone(int year, int hour) {
        super(year, hour);
    }

    @Override
    public void call(int outgoingNumber) {
        System.out.println("Calling " + outgoingNumber);
    }

    @Override
    public void ring(int incomingNumber) {
        System.out.println("Incoming call from " + incomingNumber);
    }
}
そして最後に、スマートフォン クラスがあります。これは、従来の携帯電話とは異なり、本格的なオペレーティング システムを備えています。スマートフォンのオペレーティング システム上で実行できる新しいプログラムを追加することで、スマートフォンの機能を拡張できます。コードでは、クラスは次のように記述できます。

public class Smartphone extends CellPhone {
    
    private String operationSystem;

    public Smartphone(int year, int hour, String operationSystem) {
        super(year, hour);
        this.operationSystem = operationSystem;
    }
public void install(String program) {
    System.out.println("Installing " + program + " for " + operationSystem);
}

}
ご覧のとおり、Smartphoneクラスを記述するためにかなりの量の新しいコードを作成しましたが、新しい機能を備えた新しいクラスが得られました。この OOP の原理により、必要な Java コードの量を大幅に削減できるため、プログラマの作業が楽になります。

ポリモーフィズム

さまざまな種類の電話機の外観やデザインには違いがありますが、いくつかの共通の動作を特定できます。すべての電話機は通話の受信と発信が可能であり、非常に明確でシンプルな一連のコントロールを備えています。プログラミングの観点から言えば、抽象化の原理 (これはすでによく知られています) により、電話オブジェクトには共通のインターフェイスがあると言えます。そのため、デバイスの技術的な詳細を深く掘り下げることなく、同じコントロール (機械式ボタンまたはタッチスクリーン) を備えたさまざまなモデルの電話を簡単に使用できるのです。したがって、あなたは常に携帯電話を使用しており、友人の固定電話から簡単に電話をかけることができます。オブジェクトの内部構造に関する情報がなくても、プログラムが共通のインターフェイスを持つオブジェクトを使用できるという OOP の原理は、ポリモーフィズムと呼ばれます。させて' 任意の電話を使用して別のユーザーに電話をかけることができるユーザーをプログラムで記述する必要があると想像してください。その方法は次のとおりです。

public class User {
    private String name;

    public User(String name) {
        this.name = name;
            }

    public void callAnotherUser(int number, AbstractPhone phone){
// And here's polymorphism: using the AbstractPhone type in the code!
        phone.call(number);
    }
}
 }
次に、いくつかの種類の電話機について説明します。最初の電話機の 1 つ:

public class ThomasEdisonPhone extends AbstractPhone {

public ThomasEdisonPhone(int year) {
    super(year);
}
    @Override
    public void call(int outgoingNumber) {
        System.out.println("Crank the handle");
        System.out.println("What number would you like to connect to?");
    }

    @Override
    public void ring(int incomingNumber) {
        System.out.println("The phone is ringing");
    }
}
通常の固定電話:

public class Phone extends AbstractPhone {

    public Phone(int year) {
        super(year);
    }

    @Override
    public void call(int outgoingNumber) {
        System.out.println("Calling " + outgoingNumber);
    }

    @Override
    public void ring(int incomingNumber) {
        System.out.println("The phone is ringing");
    }
}
そして最後に、クールなテレビ電話です。

public class VideoPhone extends AbstractPhone {

    public VideoPhone(int year) {
        super(year);
    }
    @Override
    public void call(int outgoingNumber) {
        System.out.println("Connecting video call to " + outgoingNumber);
    }
    @Override
    public void ring(int incomingNumber) {
        System.out.println("Incoming video call from " + incomingNumber);
    }
  }
main()メソッド でオブジェクトを作成し、callAnotherUser()メソッドをテストします。

AbstractPhone firstPhone = new ThomasEdisonPhone(1879);
AbstractPhone phone = new Phone(1984);
AbstractPhone videoPhone=new VideoPhone(2018);
User user = new User("Jason");
user.callAnotherUser(224466, firstPhone);
// Crank the handle
// What number would you like to connect to?
user.callAnotherUser(224466, phone);
// Calling 224466
user.callAnotherUser(224466, videoPhone);
// Connecting video call to 224466
ユーザーオブジェクト に対して同じメソッドを呼び出すと、異なる結果が生成されます。callメソッドの特定の実装は、プログラムの実行時に渡される特定の種類のオブジェクトに基づいてcallAnotherUser()メソッド内で動的に選択されます。これがポリモーフィズムの主な利点、つまり実行時に実装を選択できることです。上記の電話クラスの例では、メソッドのオーバーライドを使用しました。これは、メソッドのシグネチャを変更せずに、基本クラスで定義されたメソッドの実装を変更するトリックです。これは基本的にメソッドを置き換えます。プログラムの実行時に、サブクラスで定義された新しいメソッドが呼び出されます。通常、メソッドをオーバーライドするときは、@Override注釈が使用されます。これは、オーバーライドされたメソッドとオーバーライドするメソッドのシグネチャをチェックするようにコンパイラーに指示します。最後に、Java プログラムが OOP の原則と一致していることを確認するには、次のヒントに従ってください。
  • オブジェクトの主な特徴を特定します。
  • 共通のプロパティと動作を特定し、クラスの作成時に継承を使用します。
  • 抽象型を使用してオブジェクトを記述します。
  • クラスの内部実装に関連するメソッドとフィールドを常に非表示にするようにしてください。