web-dev-qa-db-ja.com

Javaで子オブジェクトに親参照を割り当てるのはなぜですか?

私は非常に簡単な質問をしていますが、これには少し混乱しています。

クラスParentがあるとします:

public class Parent {

    int name;
}

そして、別のクラスを持っていますChild.Java

public class Child extends Parent{

    int salary;
}

そして最後に私のMain.Javaクラス

public class Main {

    public static void main(String[] args)
    {
        Parent parent = new Child();
        parent.name= "abcd";
    }
}

次のような子オブジェクトを作成すると

Child child = new Child():

その後、childオブジェクトは両方のname and salary変数。

私の質問は:

Parent parent = new Child();

親クラスのname変数のみのアクセスを提供します。それで、この行の正確な使用は何ですか??

 Parent parent = new Child();

また、動的ポリモーフィズムを使用している場合、これを実行した後に子クラスの変数にアクセスできない理由

Parent parent = new Child();
56
Narendra Pal

まず、用語の明確化:ChildオブジェクトをParent型の変数に割り当てています。 Parentは、ParentChildのサブタイプであるオブジェクトへの参照です。

より複雑な例でのみ役立ちます。 getEmployeeDetailsをクラスParentに追加するとします:

public String getEmployeeDetails() {
    return "Name: " + name;
}

Childでそのメソッドをオーバーライドして、詳細を提供できます。

@Override
public String getEmployeeDetails() {
    return "Name: " + name + " Salary: " + salary;
}

これで、オブジェクトがParentであるかChildであるかに関係なく、利用可能な詳細を取得する1行のコードを記述できます。

parent.getEmployeeDetails();

次のコード:

Parent parent = new Parent();
parent.name = 1;
Child child = new Child();
child.name = 2;
child.salary = 2000;
Parent[] employees = new Parent[] { parent, child };
for (Parent employee : employees) {
    employee.getEmployeeDetails();
}

結果は次のようになります。

Name: 1
Name: 2 Salary: 2000

ChildParentとして使用しました。 Childクラスに固有の特殊な動作がありましたが、getEmployeeDetails()を呼び出したときに、その違いを無視して、ParentChildの類似性に焦点を当てることができました。 。これは subtype polymorphism と呼ばれます。

更新された質問は、ChildobjectがParent参照に格納されているときにChild.salaryにアクセスできない理由を尋ねます。答えは、「ポリモーフィズム」と「静的型付け」の共通部分です。 Javaはコンパイル時に静的に型指定されるため、コンパイラーから一定の保証が得られますが、代わりにルールに従う必要があります。そうしないと、コードがコンパイルされません。サブタイプ(例Child)のスーパータイプ(例Parent)のインスタンスとして使用できます。たとえば、employee.getEmployeeDetailsまたはemployee.nameにアクセスすると、メソッドまたはフィールドが定義されます。型employeeの変数Parentに割り当てることができるnull以外のオブジェクトでは、この保証を行うために、コンパイラはその静的型(基本的に、変数参照の型、 Parent)アクセスできるものを決定するときに、オブジェクトのランタイムタイプChildで定義されているメンバーにアクセスできません。

本当にChildParentとして使用したい場合、これは共存するのが簡単な制限であり、コードはParentとそのすべてのサブタイプで使用できます。それが受け入れられない場合は、参照の型をChildにします。

39
John Watts

共通の親インターフェースを介してすべてのサブクラスにアクセスできます。これは、すべてのサブクラスで利用可能な一般的な操作を実行するのに役立ちます。より良い例が必要です:

public class Shape
{
  private int x, y;
  public void draw();
}

public class Rectangle extends Shape
{ 
  public void draw();
  public void doRectangleAction();
}

あなたが持っている場合:

List<Shape> myShapes = new ArrayList<Shape>();

リスト内のすべてのアイテムをShapeとして参照できます。Rectangleであるか、Circleなどのその他のタイプであるかを心配する必要はありません。それらをすべて同じように扱うことができます。それらすべてを描くことができます。 Shapeが実際に長方形かどうかわからないため、doRectangleActionを呼び出すことはできません。

これは、一般的な方法でオブジェクトを処理することと、具体的に処理することの間で行われる取引です。

本当に、OOPについてもっと読む必要があると思います。良い本が役立つはずです: http://www.Amazon.com/Design-Patterns-Explained-Perspective-Object-Oriented/dp/0201715945

10
anio

親クラスをサブクラスに割り当てると、親クラスの共通機能を使用することに同意したことになります。

さまざまなサブクラス実装から抽象化する自由が得られます。その結果、親機能が制限されます。

ただし、このタイプの割り当てはアップキャストと呼ばれます。

Parent parent = new Child();  

反対はダウンキャストです。

Child child = (Child)parent;

したがって、Childのインスタンスを作成し、Parentにダウンキャストすると、その型属性nameを使用できます。 Parentのインスタンスを作成する場合、前の場合と同じことができますが、salaryにそのような属性がないため、Parentを使用できません。 salaryを使用できる前のケースに戻りますが、Childにダウンキャストする場合のみです。

詳細な説明があります

7
Roman C

プログラムをコンパイルすると、基本クラスの参照変数がメモリを取得し、コンパイラはそのクラスのすべてのメソッドをチェックします。したがって、すべての基本クラスメソッドをチェックしますが、子クラスメソッドはチェックしません。オブジェクトが作成される実行時に、チェックされたメソッドのみが実行できるようになりました。関数が実行される子クラスでメソッドがオーバーライドされる場合。コンパイラがコンパイル時にそれらを認識していないため、子クラスの他の関数は実行されません。

5

それは簡単です。

_Parent parent = new Child();
_

この場合、オブジェクトのタイプはParentです。 Ant Parentにはプロパティが1つしかありません。 nameです。

_Child child = new Child();
_

この場合、オブジェクトのタイプはChildです。 Ant Childには2つのプロパティがあります。それらはnamesalaryです。

実際には、宣言時にすぐに非最終フィールドを初期化する必要はありません。通常、これは実行時に行われます。これは、多くの場合、正確にどのような実装が必要かを正確に知ることができないためです。たとえば、クラスTransportを先頭に持つクラス階層があるとします。そして、3つのサブクラス:CarHelicopterおよびBoat。そして、フィールドTourを持つ別のクラスTransportがあります。あれは:

_class Tour {
   Transport transport;
}  
_

ユーザーが旅行を予約しておらず、特定の種類の交通機関を選択していない限り、このフィールドを初期化することはできません。最初です。

第二に、これらのクラスはすべてgo()メソッドを持っている必要がありますが、実装が異なると仮定します。スーパークラスTransportでデフォルトで基本的な実装を定義し、各サブクラスで独自の実装を所有できます。この初期化Transport tran; tran = new Car();を使用すると、メソッドtran.go()を呼び出して、特定の実装を心配することなく結果を取得できます。特定のサブクラスからオーバーライドされたメソッドを呼び出します。

さらに、スーパークラスのインスタンスが使用されるすべての場所でサブクラスのインスタンスを使用できます。たとえば、トランスポートをレンタルする機会を提供したいとします。ポリモーフィズムを使用しない場合、それぞれの場合に多くのメソッドを作成する必要があります:rentCar(Car car)rentBoat(Boat boat)など。同時に、ポリモーフィズムにより、1つの汎用メソッドrent(Transport transport)を作成できます。 Transportのサブクラスのオブジェクトを渡すことができます。さらに、時間が経つにつれてロジックが増加し、階層内に別のクラスを作成する必要がある場合はどうなりますか?多態性を使用する場合、何も変更する必要はありません。クラスTransportを拡張し、新しいクラスをメソッドに渡します。

_public class Airplane extends Transport {
    //implementation
}
_

およびrent(new Airplane())。 2番目のケースではnew Airplane().go()

5
kapand

この状況は、複数の実装がある場合に発生します。説明させてください。いくつかのソートアルゴリズムがあり、実行時に実装するアルゴリズムを選択するか、または実装を追加する機能を他の誰かに提供したいとします。この問題を解決するには、通常、抽象クラス(親)を作成し、異なる実装(子)を使用します。あなたが書く場合:

Child c = new Child();

実装をChildクラスにバインドすると、それを変更できなくなります。それ以外の場合:

Parent p = new Child();

childがParentを拡張する限り、将来コードを変更せずに変更できます。

インターフェイスを使用して同じことができます。親はクラスではなく、Javaインターフェイスです。

一般に、このアプローチは、いくつかのDB依存の実装が必要なDAOパターンで使用できます。 FactoryPatterまたはAbstractFactory Patternをご覧ください。これがあなたのお役に立てば幸いです。

2
FrancescoAzzola

Parentクラスのインスタンスの配列と、Parentを拡張する子クラスChild1、Child2、Child3のセットが必要だとしましょう。より一般的な親クラスの実装にのみ関心があり、子クラスによって導入されるより具体的なものを気にしない場合があります。

1
soliloquyy