2016-12-14 1 views
3

私はJavaの本を読んでおり、現在Polymorphismのトピックと、downcastのリファレンス変数についての本を読んでいます。しかし、私はかなりダウンキャスティングのコンセプトを理解することに固執しています。以下は私が従う例のumlです。彼らは彼らの基本給に与えられた10%の増加にあるBasePlusCommissionEmployeeのすべてのオブジェクトのために多態性とダウンキャスティングの質問

UML

。他のEmployeeサブクラスは通常通りです。 PayrollSystemTestには、アプリケーションを実行する主な方法が含まれています。

// Fig. 10.9: PayrollSystemTest.java 
// Employee hierarchy test program. 

public class PayrollSystemTest 
{ 
    public static void main(String[] args) 
    { 
     // create subclass objects 
     SalariedEmployee salariedEmployee = 
      new SalariedEmployee("John", "Smith", "111-11-1111", 800.00); 
     HourlyEmployee hourlyEmployee = 
      new HourlyEmployee("Karen", "Price", "222-22-2222", 16.75, 40.0); 
     CommissionEmployee commissionEmployee = 
      new CommissionEmployee(
      "Sue", "Jones", "333-33-3333", 10000, .06); 
     BasePlusCommissionEmployee basePlusCommissionEmployee = 
      new BasePlusCommissionEmployee(
      "Bob", "Lewis", "444-44-4444", 5000, .04, 300); 

     System.out.println("Employee processed individually:"); 

     System.out.printf("%n%s%n%s: $%,.2f%n%n", 
      salariedEmployee, "earned", salariedEmployee.earnings()); 
     System.out.printf("%s%n%s: $%,.2f%n%n", 
      hourlyEmployee, "earned", hourlyEmployee.earnings()); 
     System.out.printf("%s%n%s: $%,.2f%n%n", 
      commissionEmployee, "earned", commissionEmployee.earnings()); 
     System.out.printf("%s%n%s: $%,.2f%n%n", 
      basePlusCommissionEmployee, 
      "earned", basePlusCommissionEmployee.earnings()); 

     // create four-element Employee array 
     Employee[] employees = new Employee[4]; 

     // initialize array with Employees 
     employees[0] = salariedEmployee; 
     employees[1] = hourlyEmployee; 
     employees[2] = commissionEmployee; 
     employees[3] = basePlusCommissionEmployee; 

     System.out.printf("Employees processed polymorphically:%n%n"); 

     // generically process each element in array employees 
     for (Employee currentEmployee : employees) 
     { 
      System.out.println(currentEmployee); // invokes toString 

      // determine whether element is a BasePlusCommissionEmployee 
      if (currentEmployee instanceof BasePlusCommissionEmployee) 
      { 
       // downcast Employee reference to 
       // BasePlusCommissionEmployee reference 
       BasePlusCommissionEmployee employee = 
        (BasePlusCommissionEmployee) currentEmployee; 

       employee.setBaseSalary(1.10 * employee.getBaseSalary()); 

       System.out.printf(
        "new base salary with 10%% increase is: $%,.2f%n", 
        employee.getBaseSalary()); 
      } // end if 

      System.out.printf(
       "earned $%,.2f%n%n", currentEmployee.earnings()); 
     } // end for 

     // get type name of each object in employees array 
     for (int j = 0; j < employees.length; j++) 
      System.out.printf("Employee %d is a %s%n", j, 
       employees[j].getClass().getName()); 
    } // end main 
} // end class PayrollSystemTest 

本はさらに、ループ反復配列employeesための拡張および方法toString及び各反復におけるアレイ内の異なるEmployeeへの参照を割り当てられEmployee可変currentEmployeeearningsを起動することを説明しています。その結果、出力は、各クラスの特定のメソッドが呼び出され、実行時にオブジェクトのタイプに基づいて解決されることを示しています。現在Employeeオブジェクト上BasePlusCommissionEmployee年代方法getBaseSalarysetBaseSalaryを呼び出すために、条件ステートメントは、オブジェクト参照がのinstanceof演算子を使用して、条件が真である場合BasePlusCommissionEmployeeオブジェクトであるかどうかをチェックするために使用される

上記のメソッドを呼び出す前に、オブジェクトはダウンタイプからEmployeeBasePlusCommissionEmployeeになる必要があります。

これは真剣に、我々は、サブクラスtoStringメソッドにアクセスすることができますので、私を混乱させるが、他の方法、すなわちgetBaseSalarysetBaseSalaryを使用するためにオブジェクトをダウンキャストする必要がありますか?これはなぜですか?

答えて

3

toString()Objectで定義されているため、すべてのクラスで使用できます。ベース給与のゲッターとセッターはBasePlusCommissionEmployeeにしかないので、Employee参照(別のタイプを参照していたらどうなるでしょうか?)で呼び出すことはできません。

この例は、実際のコードでは表示されません。 instanceofを使って何をすべきかを判断することは、悪いスタイルです。

+0

うんは、私にはあまり意味があります。ところで、どうしてinstanceofの使用は悪い習慣だと言いますか? – Scorpiorian83

+0

instanceofは通常、ポリモーフィズムを適用するコードで置き換えられるため、悪い習慣です。 GhostCatの答えを参照してください。別の言い方をすると、コード 'if(obj instanceof ClassA){doA(); } else if(クラスBのobjインスタンス){doB();} } 'はClassAとClassBのスーパークラスで' doStuff() 'メソッドを定義し、それぞれのクラスでオーバーライドして正しいことを行い、上記のコードを' obj.doStuff() 'に置き換えることによって、より良く書かれます。 –

1

私の目に追加する価値がある:これは実際にはむしろの悪いの例です。

ダウンキャストは、しばしばデザインが悪いことを示しています。これはその規則をうまく証明しています。

Payrollシステムはでなく、にはこのような "instanceof"チェックが必要です。特定のサブクラスに対していくつかの特定の計算を行います。

多型を使用しての全体ポイントです:BasePlusCommissionEmployeeとCommissionEmployeeがそれぞれが正しく正しい給与を計算するために手段が含まれている必要があり、それらの異なるクラス。

ここで支えるのは、TDA(tell dont ask)の原則です。 良いオブジェクト指向は、オブジェクトのことを "約束する"ことについてです。オブジェクトに何かについて質問するのではなく、そのオブジェクトの内部状態(またはこの性質)に基づいて外部の決定を行うようにしてください!

本当にを解決する方法を理解したい方は、Robert Martinの「アジャイル原則」を参照することをお勧めします。その本は、実世界の給与計算システムの設計/実装について説明しています。

+0

あなたの知識をお寄せいただきありがとうございました。私はアジャイル原則を手に入れて、歓声を上げていきます。 – Scorpiorian83

+0

@ Scorpiorian83ヒント:あなたは、その本のC#版を無料で見つけることができます。ソースコードはあまり重要ではありません(UML図は言語に依存しません)...これが最初に読んだものです。 – GhostCat

+0

もう一度、私は本のダウンロードを終了しました。 :D – Scorpiorian83

2

インスタンスでメソッドを呼び出す場合、呼び出すメソッドは、宣言されたインスタンスの型、あなたがそれらを呼び出すところから。
あなたの例では、あなたに興味のあるインスタンスの宣言された型です。例えば

あなたはこの宣言時:

String s = new String("string"); 

をあなたはStringクラスからアクセスメソッドを呼び出すことができます。たとえば
:あなたのケースでは

s.toString(); 
s.trim(); 
etc... 

、あなたがこの宣言:

BasePlusCommissionEmployee basePlusCommissionEmployee = 
      new BasePlusCommissionEmployee(
      "Bob", "Lewis", "444-44-4444", 5000, .04, 300); 
Employee currentEmployee = basePlusCommissionEmployee; 

あなたが行うことができます:BasePlusCommissionEmployee宣言された型をするための方法を提供する basePlusCommissionEmployee.getBaseSalary()ので。

あなたも行うことができます: basePlusCommissionEmployee.toString()currentEmployee.toString()方法はObjectクラスからパブリックメソッドで、すべてのクラスがObjectクラスから継承するので、これらのクラスはtoString()を持っているので、両方のタイプ(EmployeeBasePlusCommissionEmployee)はtoString()するための方法を提供するので、方法。

currentEmployee.getBaseSalary()宣言されたタイプのEmployeeは、このメソッドを提供していないためです。

それを回避するには、ターゲットの子クラスに基本クラスからダウンキャストすることができます:あなたが言ったこと

Employee currentEmployee = basePlusCommissionEmployee; 
((BasePlusCommissionEmployee)currentEmployee).getBaseSalary(); 
+0

私に説明する貴重な時間を取ってくれたDavidに感謝します。私は最終的に理由を理解していますが、コードの最後の行については、私に見せてもらいました。それはどういう意味ですか?これについて言葉はありますか? – Scorpiorian83

+0

ようこそ。偉大なあなたを理解した:) 最後の行は、メソッドを呼び出すようにオブジェクトをキャストしたいが、キャストの結果を中間変数に格納する必要はない場合は、ショートカットです。 多分あなたを邪魔する大カッコです。 'BasePlusCommissionEmployee'型付きインスタンスに対して' getBaseSalary() 'メソッドを適用することができます。 '(BasePlusCommissionEmployee)currentEmployee.getBaseSalary()'だけを書くと、 'getBaseSalary()'が 'currentEmployee'変数に適用されるため、コンパイルエラーが発生します。参照してください。 – davidxxx

+0

クール。もう一度仲間に感謝! – Scorpiorian83