私は非常に簡単な質問をしていますが、これには少し混乱しています。
クラス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();
まず、用語の明確化:Child
オブジェクトをParent
型の変数に割り当てています。 Parent
は、Parent
、Child
のサブタイプであるオブジェクトへの参照です。
より複雑な例でのみ役立ちます。 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
Child
をParent
として使用しました。 Child
クラスに固有の特殊な動作がありましたが、getEmployeeDetails()
を呼び出したときに、その違いを無視して、Parent
とChild
の類似性に焦点を当てることができました。 。これは subtype polymorphism と呼ばれます。
更新された質問は、Child
objectがParent
参照に格納されているときにChild.salary
にアクセスできない理由を尋ねます。答えは、「ポリモーフィズム」と「静的型付け」の共通部分です。 Javaはコンパイル時に静的に型指定されるため、コンパイラーから一定の保証が得られますが、代わりにルールに従う必要があります。そうしないと、コードがコンパイルされません。サブタイプ(例Child
)のスーパータイプ(例Parent
)のインスタンスとして使用できます。たとえば、employee.getEmployeeDetails
またはemployee.name
にアクセスすると、メソッドまたはフィールドが定義されます。型employee
の変数Parent
に割り当てることができるnull以外のオブジェクトでは、この保証を行うために、コンパイラはその静的型(基本的に、変数参照の型、 Parent
)アクセスできるものを決定するときに、オブジェクトのランタイムタイプChild
で定義されているメンバーにアクセスできません。
本当にChild
をParent
として使用したい場合、これは共存するのが簡単な制限であり、コードはParent
とそのすべてのサブタイプで使用できます。それが受け入れられない場合は、参照の型をChild
にします。
共通の親インターフェースを介してすべてのサブクラスにアクセスできます。これは、すべてのサブクラスで利用可能な一般的な操作を実行するのに役立ちます。より良い例が必要です:
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
親クラスをサブクラスに割り当てると、親クラスの共通機能を使用することに同意したことになります。
さまざまなサブクラス実装から抽象化する自由が得られます。その結果、親機能が制限されます。
ただし、このタイプの割り当てはアップキャストと呼ばれます。
Parent parent = new Child();
反対はダウンキャストです。
Child child = (Child)parent;
したがって、Child
のインスタンスを作成し、Parent
にダウンキャストすると、その型属性name
を使用できます。 Parent
のインスタンスを作成する場合、前の場合と同じことができますが、salary
にそのような属性がないため、Parent
を使用できません。 salary
を使用できる前のケースに戻りますが、Child
にダウンキャストする場合のみです。
プログラムをコンパイルすると、基本クラスの参照変数がメモリを取得し、コンパイラはそのクラスのすべてのメソッドをチェックします。したがって、すべての基本クラスメソッドをチェックしますが、子クラスメソッドはチェックしません。オブジェクトが作成される実行時に、チェックされたメソッドのみが実行できるようになりました。関数が実行される子クラスでメソッドがオーバーライドされる場合。コンパイラがコンパイル時にそれらを認識していないため、子クラスの他の関数は実行されません。
それは簡単です。
_Parent parent = new Child();
_
この場合、オブジェクトのタイプはParent
です。 Ant Parent
にはプロパティが1つしかありません。 name
です。
_Child child = new Child();
_
この場合、オブジェクトのタイプはChild
です。 Ant Child
には2つのプロパティがあります。それらはname
とsalary
です。
実際には、宣言時にすぐに非最終フィールドを初期化する必要はありません。通常、これは実行時に行われます。これは、多くの場合、正確にどのような実装が必要かを正確に知ることができないためです。たとえば、クラスTransport
を先頭に持つクラス階層があるとします。そして、3つのサブクラス:Car
、Helicopter
および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()
。
この状況は、複数の実装がある場合に発生します。説明させてください。いくつかのソートアルゴリズムがあり、実行時に実装するアルゴリズムを選択するか、または実装を追加する機能を他の誰かに提供したいとします。この問題を解決するには、通常、抽象クラス(親)を作成し、異なる実装(子)を使用します。あなたが書く場合:
Child c = new Child();
実装をChildクラスにバインドすると、それを変更できなくなります。それ以外の場合:
Parent p = new Child();
childがParentを拡張する限り、将来コードを変更せずに変更できます。
インターフェイスを使用して同じことができます。親はクラスではなく、Javaインターフェイスです。
一般に、このアプローチは、いくつかのDB依存の実装が必要なDAOパターンで使用できます。 FactoryPatterまたはAbstractFactory Patternをご覧ください。これがあなたのお役に立てば幸いです。
Parentクラスのインスタンスの配列と、Parentを拡張する子クラスChild1、Child2、Child3のセットが必要だとしましょう。より一般的な親クラスの実装にのみ関心があり、子クラスによって導入されるより具体的なものを気にしない場合があります。