我正在阅读一本关于Java的书,目前正在讨论Polymorphism
主题,以及如何downcast
一个参考变量。但是,我很难理解向下转换的概念。下面是我正在关注的例子的uml。
对于BasePlusCommissionEmployee
的所有对象,他们的基本工资增加10%。其他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
本书进一步解释了增强的for循环迭代数组employees
并使用toString
变量earnings
调用方法Employee
和currentEmployee
。在每次迭代时引用数组中的不同Employee
。因此,输出说明了调用每个类的特定方法,并在执行时根据对象的类型解析。
为了在当前BasePlusCommissionEmployee
对象上调用getBaseSalary
的方法setBaseSalary
和Employee
,条件语句用于检查对象引用是否为{ {1}}对象使用 instanceof 运算符,如果条件为true,则对象必须向下转换从BasePlusCommissionEmployee
到Employee
类型调用上面提到的方法。
这让我很困惑,因为我们能够访问子类'BasePlusCommissionEmployee
方法但是必须向下转换对象才能使用其他方法toString
和getBaseSalary
?为什么会这样?
答案 0 :(得分:3)
因为toString()
中定义了Object
,因此每个班级都可以使用BasePlusCommissionEmployee
。基本工资的getter和setter仅在Employee
中可用,因此您无法通过private static void getSubjectHash( X509Certificate x509Cert )
{
try {
// get the subject principal
X500Principal x500Princ = x509Cert.getSubjectX500Principal( );
// create a new principal using canonical name (order, spacing, etc.) and get it in ANS1 DER format
byte[] newPrincEnc = new X500Principal( x500Princ.getName( X500Principal.CANONICAL ) ).getEncoded( );
// read it in as an ASN1 Sequence to avoid custom parsing
ASN1InputStream aIn = new ASN1InputStream( newPrincEnc );
ASN1Sequence seq = (ASN1Sequence) aIn.readObject( );
List<byte[]> terms = new ArrayList<>( );
int finalLen = 0;
int i = 0;
// hash the encodables for each term individually and accumulate them in a list
for ( ASN1Encodable asn1Set : seq.toArray( ) ) {
byte[] term = ( (ASN1Set) asn1Set ).getEncoded( );
term[9] = 0x0c; // change tag from 0x13 (printable string) to 0x0c
terms.add( term );
finalLen += term.length;
// digest the term
byte[] hashBytes = truncatedHash( getDigest( term ), 4 );
printByteArray( String.format( "hash of object at %d:", i++ ), hashBytes );
System.out.println( "" );
}
// hash all terms together in order of appearance
int j = 0;
byte[] finalEncForw = new byte[finalLen];
for ( byte[] term : terms )
for ( byte b : term )
finalEncForw[j++] = b;
// digest and truncate
byte[] hashBytes = truncatedHash( getDigest( finalEncForw ), 4 );
printByteArray( "hash of all terms in forward order", hashBytes );
System.out.println( "" );
}
catch ( Exception ex ) {
throw new RuntimeException( "uh-oh" );
}
}
引用调用它(如果引用不同类型会发生什么情况?)。
这个例子不是你在实际代码中看到的。使用instanceof确定要做什么是糟糕的风格。
答案 1 :(得分: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.getBaseSalary()
因为BasePlusCommissionEmployee
声明的类型为。提供了方法
你也可以这样做:
basePlusCommissionEmployee.toString()
和currentEmployee.toString()
,因为这两种类型(Employee
和BasePlusCommissionEmployee
)都为toString()
提供了方法,因为该方法是来自Object
类的公共方法并且所有类都继承自Object
类,因此这些类具有toString()
方法。
但你做不到:
currentEmployee.getBaseSalary()
因为Employee
声明的类型不提供。
要绕过它,您可以从基类转发到目标子类:
Employee currentEmployee = basePlusCommissionEmployee;
((BasePlusCommissionEmployee)currentEmployee).getBaseSalary();
答案 2 :(得分:1)
值得加入我的眼中:这实际上是一个相当坏的例子。
downcast 经常表明设计不好;这很好地证明了这一规则。
薪资系统应该不需要这样的“instanceof”检查;然后为特定的子类做一些特定的计算。
这是使用多态的整个点:那些不同的类BasePlusCommissionEmployee和CommissionEmployee应该包含正确计算正确工资的方法。
这里要坚持的是TDA(告诉不要问)原则。 好的OO是关于告诉某些对象“做那件事”;而不是向对象询问某事,然后根据该对象的内部状态(或本例中的性质)做出外部决定!
对于任何有兴趣了解如何真正解决此问题的人,我建议查看Robert Martin的“敏捷原则”。那本书描述了现实世界薪资系统的设计/实施......