演示网站,crm客户管理,邯郸移动网站建设,西安哪家网站建设好编者注 #xff1a;在本文中#xff0c;我们提供了Java教程中的全面抽象。 抽象发生在类级别的设计中#xff0c;目的是隐藏实现API /设计/系统提供的功能的方式的实现复杂性#xff0c;从某种意义上讲简化了访问底层实现的“接口”。 此过程可以在越来越“更高”的抽象层次… 编者注 在本文中我们提供了Java教程中的全面抽象。 抽象发生在类级别的设计中目的是隐藏实现API /设计/系统提供的功能的方式的实现复杂性从某种意义上讲简化了访问底层实现的“接口”。 此过程可以在越来越“更高”的抽象层次层上重复进行从而可以在较大的层上构建大型系统而不会增加代码和理解的复杂性。 目录 1.简介 2.接口 2.1。 定义接口 2.2。 实施接口 2.3。 使用介面 3.抽象类 3.1。 定义抽象类 3.2。 扩展抽象类 3.3。 使用抽象类 4.一个可行的例子–付款系统 4.1。 收款人界面 4.2。 付款系统 4.3。 员工班 4.4。 应用程序 4.5。 处理红利 4.6。 承包公司 4.7。 先进的功能税收 5.结论 1.简介 在本教程中我们将介绍Java中的抽象并使用接口抽象类和具体类定义一个简单的薪资系统。 Java有两种抽象级别- 接口 用于定义预期的行为和抽象类 用于定义不完整的功能。 现在我们将详细介绍这两种不同类型的抽象。 2.接口 接口就像合同。 承诺提供某些行为并且所有实现接口保证的类也将实现那些行为。 为了定义预期的行为接口列出了许多方法签名。 任何使用该接口的类都可以依赖那些在实现该接口的运行时类中实现的方法。 这样使用该界面的任何人都可以知道将提供什么功能而不必担心该功能将如何实现。 客户端看不到实现细节这是抽象的关键好处。 定义接口 您可以使用关键字interface定义一个接口 public interface MyInterface {void methodA();int methodB();String methodC(double x, double y);} 在这里我们看到一个定义为MyInterface的接口请注意对接口应该使用与对类相同的大小写约定。 MyInterface定义了3种方法每种方法具有不同的返回类型和参数。 您可以看到这些方法都没有主体。 当使用接口时我们只对定义预期的行为感兴趣而与实现无关。 注意Java 8引入了为接口方法创建默认实现的功能但是在本教程中我们将不介绍该功能。 接口还可以通过使用成员变量来包含状态数据 public interface MyInterfaceWithState {int someNumber;void methodA();} 默认情况下接口中的所有方法都是公共的实际上您不能在具有非public访问级别的接口中创建方法。 实施接口 现在我们定义了一个我们要创建的类的接口该类将提供已定义行为的实现细节。 为此我们编写了一个新类并使用implements关键字告诉编译器该类应实现什么接口。 public class MyClass implements MyInterface {public void methodA() {System.out.println(Method A called!);}public int methodB() {return 42;}public String methodC(double x, double y) {return x x , y y;}} 我们采用了在MyInterface定义的方法签名并为它们提供了实现它们的主体。 我们只是在实现中做了一些任意的愚蠢之举但必须注意的是只要它们满足方法签名我们就可以在这些主体中做任何事情。 我们还可以根据需要创建尽可能多的实现类每个实现类都具有MyInterface方法的不同实现体。 我们在MyClass实现了MyInterface中的所有方法如果我们未能实现它们中的任何一个则编译器将给出一个错误。 这是因为MyClass实现MyInterface的事实意味着MyClass保证为MyInterface中的每个方法提供实现。 这样使用该接口的任何客户端都可以依靠这样的事实在运行时可以保证要调用的方法已经实现。 使用介面 要从客户端调用接口的方法我们只需要使用点。运算符就像使用类的方法一样 MyInterface object1 new MyClass();
object1.methodA(); // Guaranteed to work 我们在上面看到了一些不寻常的东西而不是像MyClass object1 new MyClass();这样的东西MyClass object1 new MyClass(); 这完全可以接受我们将object1声明为MyInterface类型。 之所以可行是因为MyClass是MyInterface的实现无论何时我们要调用MyInterface中定义的方法我们都知道MyClass将提供实现。 object1是对实现MyInterface的任何运行时对象的引用 在这种情况下它是MyClass的实例。 如果我们尝试做MyInterface object1 new MyInterface()我们将得到一个编译器错误因为您无法实例化一个接口这是有道理的因为该接口中没有实现细节也没有执行代码。 当我们调用object1.methodA()我们正在执行MyClass中定义的方法主体因为对象1的运行时类型是MyClass即使引用的类型是MyInterface。 我们只能在MyInterface中定义的object1上调用方法出于所有目的和目的即使运行时类型为MyClass我们也可以将object1称为MyInterface类型。 实际上如果MyClass定义了另一个名为methodD()方法我们将无法在object1上调用它因为编译器仅知道object1是对MyInterface的引用而不是它专门是MyClass。 这个重要的区别使我们能够为接口创建不同的实现类而不必担心在运行时调用哪个特定的实现类。 使用以下界面 public interface OneMethodInterface {void oneMethod();} 它定义了一个不带参数的void方法。 让我们实现它 public class ClassA implements OneMethodInterface {public void oneMethod() {System.out.println(Runtime type is ClassA.);}
} 我们可以像以前一样在客户端中使用它 OneMethodInterface myObject new ClassA();
myObject.oneMethod(); 输出 Runtime type is ClassA. 现在让我们为OneMethodInterface做一个不同的实现 public class ClassB implements OneMethodInterface {public void oneMethod() {System.out.println(The runtime type of this class is ClassB.);
}
} 并修改上面的代码 OneMethodInterface myObject new ClassA();
myObject.oneMethod();
myObject new ClassB();
myObject.oneMethod(); 输出 Runtime type is ClassA.
The runtime type of this class is ClassB. 我们已经成功地使用相同的Reference myObject 来引用两种不同运行时类型的实例。 实际的实现对编译器而言完全不重要它只是在乎OneMethodInterface是否以任何方式和以任何方式实现。 就编译器而言myObject是OneMethodInterface并且oneMethod()可用即使oneMethod()其重新分配给不同类类型的其他实例对象也是如此。 这种提供多种运行时类型并在运行时而不是编译时解析的功能称为多态性 。 接口定义行为时没有任何实现细节忽略Java 8实现类定义了它们定义的类的所有实现细节但是如果我们希望将这两个概念混合使用会怎样 如果要在同一位置混合一些行为定义和一些实现则可以使用抽象类。 3.抽象类 抽象类就像一个不完整的蓝图它定义了该类的一些实现细节而另一些则保留为稍后要实现的简单行为定义。 想象一下一个房子的蓝图其中的房子被完全画进去了但是有一个大的空旷的正方形可以停放车库。 我们知道会有车库但我们不知道它会是什么样。 其他人将需要复制我们的蓝图并在车库中绘制。 实际上几个不同的人可能会复制我们的蓝图并绘制不同类型的车库。 使用这些蓝图建造的房屋都是我们房屋的可识别变体。 前门房间布局和窗户将完全相同但是车库将完全不同。 就像我们抽象类上面的蓝图将完全定义一些方法一样这些方法实现在抽象类的所有实现中都是相同的。 抽象类将仅为其他方法定义签名其方式与接口的定义大致相同。 这些方法的方法实现在实现类中会有所不同。 抽象类的实现类通常称为具体类。 由于具体类和抽象类之间的继承关系我们通常说具体类扩展了抽象类而不是像我们所说的那样通过接口实现它。 就像使用接口一样任何客户端代码都知道如果具体类正在扩展抽象类则具体类将保证为抽象类的抽象方法提供方法体抽象类为非抽象方法提供了它自己的方法体。课程。 同样就像接口一样给定抽象类可以有几个不同的具体类每个类可以在满足抽象类协定的同时为抽象类的抽象方法定义非常不同的行为。 实施细节对客户端隐藏。 定义抽象类 关键字abstract用于将类及其方法定义为abstract。 public abstract class MyAbstractClass {protected int someNumber 0;public void increment() {someNumber;}public abstract void doSomethingWithNumber();} 在这里我们定义了一个名为MyAbstractClass的抽象类该类包含一个整数并提供了递增该整数的方法。 我们还定义了一个名为doSomethingWithNumber()的抽象方法。 我们尚不知道此方法会做什么它将在扩展MyAbstractClass任何具体类中MyAbstractClass 。 doSomethingWithNumber()没有方法主体因为它是抽象的。 在接口中默认情况下所有方法都是公共的但是抽象方法的范围可以是公共的打包的或受保护的。 您可以看到在此抽象类中increment中的某些行为实现与doSomethingWithNumber()中的某些行为定义混合在一起。 抽象类将某种实现与某种定义混合在一起。 扩展Abstract类的具体类将重用increment()的实现同时保证提供自己的doSomethingWithNumber()的实现。 扩展抽象类 现在我们已经创建了一个抽象类让我们对其进行具体实现。 我们通过在本身不是抽象的类中使用extend关键字从抽象类中进行具体实现。 public class MyConcreteClass extends MyAbstractClass {public void sayHello() {System.out.println(Hello there!);
}public void doSomethingWithNumber() {System.out.println(The number is someNumber);
}} 我们创建了一个名为MyConcreteClass的具体类并扩展了MyAbstractClass 。 我们只需要提供抽象方法doSomethingWithNumber()的实现因为我们继承了MyAbstractClass的非私有成员变量和方法。 如果有任何客户端在MyConcreteClass上调用increment() 则将执行MyAbstractClass中定义的实现。 我们还创建了一个名为sayHello()的新方法该方法是MyConcreteClass所独有的其他实现MyAbstractClass的具体类都无法使用。 我们还可以使用不实现doSomethingWithNumber另一个抽象类来扩展MyAbstractClass 这意味着必须定义另一个具体的类该类将扩展这个新类以便实现doSomethingWithNumber() 。 public abstract class MyOtherAbstractClass extends MyAbstractClass {public void sayHello() {System.out.println(Hello there!);
}
} 在这里我们不必对doSomethingWithNumber进行任何引用只要为MyOtherAbstractClass创建一个具体的类我们便会为doSomethingWithNumber提供实现。 最后抽象类本身可以实现接口。 由于抽象类本身无法实例化因此不必为所有或任何接口方法提供实现。 如果抽象类未提供接口方法的实现则扩展抽象类的具体类将必须提供实现。 public abstract MyImplementingAbstractClass implements MyInterface {public void methodA() {System.out.println(Method A has been implemented in this abstract class);}
} 在这里我们看到MyImplementingAbstractClass实现MyInterface但仅提供methodA的实现。 如果任何具体的类扩展了MyImplementingAbstractClass它将必须提供MyInterface中定义的methodB和methodC的实现。 使用抽象类 就像接口和常规类一样要使用抽象。运算符来调用抽象类的方法。 MyAbstractClass object1 new MyConcreteClass();
object1.increment();
object1.doSomethingWithNumber(); 再次我们看到object1是对实例的引用该实例为MyAbstractClass提供了具体的实现并且该实例的运行时类型为MyConcreteClass。 出于所有目的编译器将object1视为MyAbstractClass实例。 如果尝试调用MyConcreteClass中定义的sayHello()方法则会收到编译器错误。 通过object1此方法对编译器不可见 因为object1是MyAbstractClass引用。 object1唯一提供的保证是它将具有MyAbstractClass中定义的方法的实现运行时类型提供的任何其他方法都不可见。 object1.sayHello(); // compiler error 与接口一样我们可以提供不同的运行时类型并通过相同的引用使用它们。 让我们定义一个新的抽象类 public abstract class TwoMethodAbstractClass {public void oneMethod() {System.out.prinln(oneMethod is implemented in TwoMethodAbstractClass.);}public abstract void twoMethod();
} 它定义了一个实现的方法和另一个抽象的方法。 让我们用一个具体的类来扩展它 public class ConcreteClassA extends TwoMethodAbstractClass {public void twoMethod() {System.out.println(twoMethod is implemented in ConcreteClassA.);
}
} 我们可以像以前一样在客户端中使用它 TwoMethodAbstractClass myObject new ConcreteClassA();
myObject.oneMethod();
myObject.twoMethod(); 输出 oneMethod is implemented in TwoMethodAbstractClass.
twoMethod is implemented in ConcreteClassA. 现在让我们做一个扩展TwoMethodClass的不同的具体类 public class ConcreteClassB extends TwoMethodAbstractClass {public void twoMethod() {System.out.println(ConcreteClassB implements its own twoMethod.);}
} 并修改上面的代码 TwoMethodAbstractClass myObject new ConcreteClassA();
myObject.oneMethod();
myObject.twoMethod();
myObject new ConcreteClassB();
myObject.oneMethod();
myObject.twoMethod(); 输出 oneMethod is implemented in TwoMethodAbstractClass.
twoMethod is implemented in ConcreteClassA.
oneMethod is implemented in TwoMethodAbstractClass.
ConcreteClassB implements its own twoMethod. 我们使用相同的引用myObject来引用两种不同的运行时类型的实例。 当myObject的运行时类型为ConcreteClassA时它将使用ConcreteClassA中的twoMethod的实现。 当myObject的运行时类型为ConcreteClassB时它将使用ConcreteClassB中的twoMethod的实现。 在这两种情况下都使用TwoMethodAbstractClass中的oneMethod的通用实现。 抽象类用于定义常见行为同时提供合同或承诺以后可以从具体类中获得其他行为。 这使我们能够建立对象模型在其中可以重用通用功能并捕获不同具体类中的不同需求。 4.一个可行的例子–付款系统 我们被要求为公司建立付款系统。 我们知道公司有不同类型的员工可以通过不同的方式获得报酬。 薪水和佣金。 该公司的老板知道他的需求将会改变并且该系统可能会在以后更改以适应将要接收付款的不同类型的实体。 收款人界面 让我们开始考虑员工。 他们必须收到付款但我们也知道以后可能需要让其他实体也收到付款。 让我们创建一个接口Payee该接口将定义我们期望可以接收付款的实体所采取的行为。 public interface Payee {String name();Double grossPayment();Integer bankAccount();
} 在这里我们有一个Payee界面可以保证三种行为 提供收款人姓名的能力提供应支付的总付款额的能力以及提供应将资金存入的银行帐号的能力。 付款系统 现在我们定义了一个收款人让我们编写一些使用它的代码。 我们将创建一个PaymentSystem类该类维护一个收款人列表按需将在其间循环并将请求的金额转入相应的银行帐户。 public class PaymentSystem {private ListPayee payees;public PaymentSystem() {payees new ArrayList();}public void addPayee(Payee payee) {if (!payees.contains(payee)) {payees.add(payee);}}public void processPayments() {for (Payee payee : payees) {Double grossPayment payee.grossPayment();System.out.println(Paying to payee.name());System.out.println(\tGross\t grossPayment);System.out.println(\tTransferred to Account: payee.bankAccount());}}
} 你可以看到PaymentSystem是完全不可知的运行时类型的收款人的它并不关心没有关心。 它知道无论正在处理的收款人是哪种运行时类型都可以保证实现name() grossPayment()和bankAccount() 。 有了这些知识只需在所有收款人之间执行for循环并使用这些方法处理其付款即可。 员工班 老板告诉我们他有两种不同类型的员工-薪水员工和特级员工。 受薪雇员的基本工资不会改变而受雇雇员的基本工资不会改变并且可以获得成功销售的销售佣金。 4.3.1 SalaryEmployee类 让我们从“受薪雇员”课程开始。 它应该实现“收款人”界面以便支付系统可以使用它。 让我们使用代码之外的类图来表示更改。 在类图中虚线箭头表示“实现”关系实线箭头表示“扩展”关系。 类图 图1 码 public class SalaryEmployee implements Payee {private String name;private Integer bankAccount;protected Double grossWage;public SalaryEmployee(String name, Integer bankAccount, Double grossWage) {this.name name;this.bankAccount bankAccount;this.grossWage grossWage;}public Integer bankAccount() {return bankAccount;}public String name() {return name;}public Double grossPayment() {return grossWage;}
}4.3.2 CommissionEmployee类 现在让我们创建一个CommissionEmployee类。 该类与具有支付佣金能力的SalaryEmployee非常相似。 类图 图2 码 public class CommissionEmployee implements Payee {private String name;private Integer bankAccount;protected Double grossWage;private Double grossCommission 0.0;public CommissionEmployee(String name, Integer bankAccount, Double grossWage) {this.name name;this.bankAccount bankAccount;this.grossWage grossWage;}public Integer bankAccount() {return bankAccount;}public String name() {return name;}public Double grossPayment() {return grossWage doCurrentCommission();}private Double doCurrentCommission() {Double commission grossCommission;grossCommission 0.0;return commission;}public void giveCommission(Double amount) {grossCommission amount;}
} 如您所见SalaryEmployee和CommissionEmployee之间有很多代码重复。 实际上唯一不同的是GrossPayment的计算该计算使用CommissionEmployee中的佣金值。 其中一些功能相同而另一些不同。 这看起来像是抽象类的不错候选人。 4.3.3员工抽象类 让我们将名称和银行帐户功能抽象为Employee抽象类。 然后SalaryEmployee和CommissionEmployee可以扩展此抽象类并为grossPayment()提供不同的实现。 类图 图3 码 public abstract class Employee implements Payee {private String name;private Integer bankAccount;protected Double grossWage;public Employee(String name, Integer bankAccount, Double grossWage) {this.name name;this.bankAccount bankAccount;this.grossWage grossWage;}public String name() {return name;}public Integer bankAccount() {return bankAccount;}
} 请注意由于Employee是抽象的因此Employee不必实现在Payee中定义的grossPayment方法。 现在我们重写两个Employee类 public class SalaryEmployee extends Employee {public SalaryEmployee(String name, Integer bankAccount, Double grossWage) {super(name, bankAccount, grossWage);}public Double grossPayment() {return grossWage;}
}public class CommissionEmployee extends Employee {private Double grossCommission 0.0;public CommissionEmployee(String name, Integer bankAccount, Double grossWage) {super(name, bankAccount, grossWage);}public Double grossPayment() {return grossWage doCurrentCommission();}private Double doCurrentCommission() {Double commission grossCommission;grossCommission 0.0;return commission;}public void giveCommission(Double amount) {grossCommission amount;}
} 更整洁了 应用程序 让我们尝试在应用程序中使用新的Employee类型。 我们将创建一个应用程序类该应用程序类通过创建支付系统一些员工并模拟一周的工作来初始化系统。 4.4.1 PaymentApplication类 应用程序类如下所示 public class PaymentApplication {public static void main(final String... args) {// InitializationPaymentSystem paymentSystem new PaymentSystem();CommissionEmployee johnSmith new CommissionEmployee(John Smith, 1111, 300.0);paymentSystem.addPayee(johnSmith);CommissionEmployee paulJones new CommissionEmployee(Paul Jones, 2222, 350.0);paymentSystem.addPayee(paulJones);SalaryEmployee maryBrown new SalaryEmployee(Mary Brown, 3333, 500.0);paymentSystem.addPayee(maryBrown);SalaryEmployee susanWhite new SalaryEmployee(Susan White, 4444, 470.0);paymentSystem.addPayee(susanWhite);// Simulate WeekjohnSmith.giveCommission(40.0);johnSmith.giveCommission(35.0);johnSmith.giveCommission(45.0);paulJones.giveCommission(45.0);paulJones.giveCommission(51.0);paulJones.giveCommission(23.0);paulJones.giveCommission(14.5);paulJones.giveCommission(57.3);// Process Weekly PaymentpaymentSystem.processPayments();} 我们创建了两个委托雇员和两个受薪雇员每个雇员都有自己的姓名基本工资和银行帐号。 我们将每个员工加载到我们创建的支付系统中。 然后我们通过给两名委托的雇员提供佣金来模拟一周然后要求付款系统处理一周中的所有付款。 输出 Paying to John SmithGross 420.0Transferred to Account: 1111
Paying to Paul JonesGross 540.8Transferred to Account: 2222
Paying to Mary BrownGross 500.0Transferred to Account: 3333
Paying to Susan WhiteGross 470.0Transferred to Account: 4444处理红利 到目前为止老板对这个系统非常满意但是他告诉我们为了激励他的员工他希望有能力给他们每周的百分比奖金。 他告诉我们由于受托员工的薪水通常较低因此我们应该给他们1.5倍的奖金乘数以提高他们的奖金百分比。 奖金应反映在每位员工的总工资中。 4.5.1员工奖金类别 让我们向员工添加一个字段以捕获给定的任何奖金使用受保护的方法来返回并重置奖金并使用抽象方法来提供奖金。 doBonus()方法受到保护以便具体的Employee类可以访问它。 GiveBonus方法是抽象的因为它将对受薪雇员和委托雇员实施不同的方法。 public abstract class Employee implements Payee {private String name;private Integer bankAccount;protected Double currentBonus;protected Double grossWage;public Employee(String name, Integer bankAccount, Double grossWage) {this.name name;this.bankAccount bankAccount;this.grossWage grossWage;currentBonus 0.0;}public String name() {return name;}public Integer bankAccount() {return bankAccount;}public abstract void giveBonus(Double percentage);protected Double doCurrentBonus() {Double bonus currentBonus;currentBonus 0.0;return bonus;}
} 对SalaryEmployee的更新 public class SalaryEmployee extends Employee {public SalaryEmployee(String name, Integer bankAccount, Double grossWage) {super(name, bankAccount, grossWage);}public void giveBonus(Double percentage) {currentBonus grossWage * (percentage/100.0);}public Double grossPayment() {return grossWage doCurrentBonus();}
} 对CommissionEmployee的更新 public class CommissionEmployee extends Employee {private static final Double bonusMultiplier 1.5;private Double grossCommission 0.0;public CommissionEmployee(String name, Integer bankAccount, Double grossWage) {super(name, bankAccount, grossWage);}public void giveBonus(Double percentage) {currentBonus grossWage * (percentage/100.0) * bonusMultiplier;}public Double grossPayment() {return grossWage doCurrentBonus() doCurrentCommission();}private Double doCurrentCommission() {Double commission grossCommission;grossCommission 0.0;return commission;}public void giveCommission(Double amount) {grossCommission amount;}
} 我们可以看到两个类在实现GiveBonus方面有所不同– CommissionEmployee使用奖金乘数。 我们还可以看到在计算总付款时这两个类都使用了在Employee中定义的受保护的doCurrentBonus方法。 让我们更新应用程序以模拟向员工支付每周奖金的方法 public class PaymentApplication {public static void main(final String... args) {// InitializationPaymentSystem paymentSystem new PaymentSystemV1();CommissionEmployee johnSmith new CommissionEmployee(John Smith, 1111, 300.0);paymentSystem.addPayee(johnSmith);CommissionEmployee paulJones new CommissionEmployee(Paul Jones, 2222, 350.0);paymentSystem.addPayee(paulJones);SalaryEmployee maryBrown new SalaryEmployee(Mary Brown, 3333, 500.0);paymentSystem.addPayee(maryBrown);SalaryEmployee susanWhite new SalaryEmployee(Susan White, 4444, 470.0);paymentSystem.addPayee(susanWhite);// Simulate WeekjohnSmith.giveCommission(40.0);johnSmith.giveCommission(35.0);johnSmith.giveCommission(45.0);johnSmith.giveBonus(5.0);paulJones.giveCommission(45.0);paulJones.giveCommission(51.0);paulJones.giveCommission(23.0);paulJones.giveCommission(14.5);paulJones.giveCommission(57.3);paulJones.giveBonus(6.5);maryBrown.giveBonus(3.0);susanWhite.giveBonus(7.5);// Process Weekly PaymentpaymentSystem.processPayments();}
} 输出 Paying to John SmithGross 442.5Transferred to Account: 1111
Paying to Paul JonesGross 574.925Transferred to Account: 2222
Paying to Mary BrownGross 515.0Transferred to Account: 3333
Paying to Susan WhiteGross 505.25Transferred to Account: 4444 毛额现在反映了支付给员工的奖金。 承包公司 老板对支付系统很满意但是他想到了需要支付的其他人。 他将不时聘请承包公司的服务。 显然这些公司没有工资也没有支付任何奖金。 他们可以得到几笔一次性付款在付款系统处理时应向其支付所有累计付款并结清其余额。 承包公司类 ContractingCompany类应实现Payee以便付款系统可以使用它。 它应该有一种支付服务的方法该方法将通过累计总数进行跟踪并用于支付。 类图 图4 码 public class ContractingCompany implements Payee {private String name;private Integer bankAccount;private Double currentBalance;public ContractingCompany(String name, Integer bankAccount) {this.name name;this.bankAccount bankAccount;currentBalance 0.0;}public String name() {return name;}public Double grossPayment() {return doPayment();}private Double doPayment() {Double balance currentBalance;currentBalance 0.0;return balance;}public Integer bankAccount() {return bankAccount;}public void payForServices(Double amount) {currentBalance amount;}
} 现在让我们在我们的“付款应用程序”中添加几个签约公司并模拟向他们的一些付款 public class PaymentApplication {public static void main(final String... args) {// InitializationPaymentSystem paymentSystem new PaymentSystemV1();CommissionEmployee johnSmith new CommissionEmployee(John Smith, 1111, 300.0, 100.0);paymentSystem.addPayee(johnSmith);CommissionEmployee paulJones new CommissionEmployee(Paul Jones, 2222, 350.0, 125.0);paymentSystem.addPayee(paulJones);SalaryEmployee maryBrown new SalaryEmployee(Mary Brown, 3333, 500.0, 110.0);paymentSystem.addPayee(maryBrown);SalaryEmployee susanWhite new SalaryEmployee(Susan White, 4444, 470.0, 130.0);paymentSystem.addPayee(susanWhite);ContractingCompany acmeInc new ContractingCompany(Acme Inc, 5555);paymentSystem.addPayee(acmeInc);ContractingCompany javaCodeGeeks new ContractingCompany(javacodegeeks.com, 6666);paymentSystem.addPayee(javaCodeGeeks);// Simulate WeekjohnSmith.giveCommission(40.0);johnSmith.giveCommission(35.0);johnSmith.giveCommission(45.0);johnSmith.giveBonus(5.0);paulJones.giveCommission(45.0);paulJones.giveCommission(51.0);paulJones.giveCommission(23.0);paulJones.giveCommission(14.5);paulJones.giveCommission(57.3);paulJones.giveBonus(6.5);maryBrown.giveBonus(3.0);susanWhite.giveBonus(7.5);acmeInc.payForServices(100.0);acmeInc.payForServices(250.0);acmeInc.payForServices(300.0);javaCodeGeeks.payForServices(400.0);javaCodeGeeks.payForServices(250.0);// Process Weekly PaymentpaymentSystem.processPayments();} 输出 Paying to John SmithGross 442.5Transferred to Account: 1111
Paying to Paul JonesGross 574.925Transferred to Account: 2222
Paying to Mary BrownGross 515.0Transferred to Account: 3333
Paying to Susan WhiteGross 505.25Transferred to Account: 4444
Paying to Acme IncGross 650.0Transferred to Account: 5555
Paying to javacodegeeks.comGross 650.0Transferred to Account: 6666 现在我们已经成功地向系统中添加了一种全新的收款人类型而无需更改处理收款人的PaymentSystem类中的一行代码。 这就是抽象的力量。 先进的功能税收 老板正在使用支付系统上空但是收税员给他发了一封信告诉他必须将某种预扣税纳入系统中否则会遇到大麻烦。 该系统应该有一个全球税率并且每个雇员都应该有个人免税额。 仅应在向员工支付的免税津贴之外的任何款项中收税。 然后应仅向雇员支付应付给他们的净付款额。 订约公司负责缴纳自己的税款因此系统不应向其代扣代缴税款。 为了处理税收我们需要创建一种新的特殊收款人类型该收款人应纳税并且可以提供免税额度。 在Java中我们可以扩展接口。 这让我们在不修改原始接口的情况下添加行为定义。 原始接口中定义的所有行为将自动带入新接口。 TaxablePayee界面 我们将扩展Payee以创建新的TaxablePayee接口然后让Employee实现该接口同时让ContractingCompany保持为常规的免税Payee。 类图 图5 码 public interface TaxablePayee extends Payee {public Double allowance();} 在这里我们看到Payee进行了扩展并定义了另一个方法– allowance() 该方法返回TaxablePayee的免税津贴。 现在我们需要更新Employee以实现TaxablePayee我们将看到这将对两个具体的Employee类产生影响。 public abstract class Employee implements TaxablePayee {private String name;private Integer bankAccount;private Double allowance;protected Double currentBonus;protected Double grossWage;public Employee(String name, Integer bankAccount, Double grossWage, Double allowance) {this.name name;this.bankAccount bankAccount;this.grossWage grossWage;this.allowance allowance;currentBonus 0.0;}public String name() {return name;}public Integer bankAccount() {return bankAccount;}public Double allowance() {return allowance;}public abstract void giveBonus(Double percentage);protected Double doCurrentBonus() {Double bonus currentBonus;currentBonus 0.0;return bonus;}} 我们不得不更改Employee的构造函数这意味着我们两个抽象类的构造函数也将需要更改。 public class SalaryEmployee extends Employee {public SalaryEmployee(String name, Integer bankAccount, Double grossWage, Double allowance) {super(name, bankAccount, grossWage, allowance);}Overridepublic void giveBonus(Double percentage) {currentBonus grossWage * (percentage/100.0);}Overridepublic Double grossPayment() {return grossWage doCurrentBonus();}
} 佣金员工 public class CommissionEmployee extends Employee {private static final Double bonusMultiplier 1.5;private Double grossCommission 0.0;public CommissionEmployee(String name, Integer bankAccount, Double grossWage, Double allowance) {super(name, bankAccount, grossWage, allowance);}Overridepublic void giveBonus(Double percentage) {currentBonus grossWage * (percentage/100.0) * bonusMultiplier;}Overridepublic Double grossPayment() {return grossWage doCurrentBonus() doCurrentCommission();}private Double doCurrentCommission() {Double commission grossCommission;grossCommission 0.0;return commission;}public void giveCommission(Double amount) {grossCommission amount;}
}PaymentSystem中的税收变更 现在我们需要更新PaymentSystem以预扣税并且为此我们需要某种方式来确定给定的收款人是TaxablePayee还是常规的收款人。 我们可以利用一个名为instanceof的Java关键字来做到这一点。 Instanceof让我们检查给定引用变量的运行时类型是否与测试类型匹配。 它返回一个布尔值可以这样调用ifMyClass的object1 instance。 如果object1的运行时类型是MyClass 或 MyClass 的子类或实现类如果MyClass是接口 则返回true。 这意味着我们可以在继承树上的任何地方进行测试使其成为非常强大的工具。 我们可以使用此工具来确定给定的收款人是否是TaxablePayee的实例并根据该知识采取适当的措施。 现在我们按以下方式更新PaymentSystem public class PaymentSystem {private ListPayee payees;private Double taxRate 0.2;public PaymentSystem() {payees new ArrayList();}public void addPayee(Payee payee) {if (!payees.contains(payee)) {payees.add(payee);}}public void processPayments() {for (Payee payee : payees) {Double grossPayment payee.grossPayment();Double tax 0.0;if (payee instanceof TaxablePayee) {Double taxableIncome grossPayment - ((TaxablePayee)payee).allowance();tax taxableIncome * taxRate;}Double netPayment grossPayment - tax;System.out.println(Paying to payee.name());System.out.println(\tGross\t grossPayment);System.out.println(\tTax\t\t- tax);System.out.println(\tNet\t\t netPayment);System.out.println(\tTransferred to Account: payee.bankAccount());}}
} 如果收款人被处理的新代码首先检查是TaxablePayee的一个实例如果是则它在收款人的铸造把它作为参考以一个TaxablePayee用于访问的目的allowance()在TaxablePayee定义的方法。 记得; 如果引用保留为收款人则allowance()方法在PayableSystem中不可见因为它是在TaxablePayee中定义的而不是在Payee中定义的。 由于我们已经确认收款人是TaxablePayee的一个实例因此此处的转换是安全的。 现在PaymentSystem有了免税额它可以根据20的全球税率计算应纳税额和要预扣的税款。 如果收款人不是TaxablePayee则系统会继续正常处理它们而不进行与税务处理有关的任何操作。 让我们更新应用程序类为我们的员工提供不同的免税额度然后再次执行该应用程序 public class PaymentApplication {public static void main(final String... args) {// InitializationPaymentSystem paymentSystem new PaymentSystemV1();CommissionEmployee johnSmith new CommissionEmployee(John Smith, 1111, 300.0, 100.0);paymentSystem.addPayee(johnSmith);CommissionEmployee paulJones new CommissionEmployee(Paul Jones, 2222, 350.0, 125.0);paymentSystem.addPayee(paulJones);SalaryEmployee maryBrown new SalaryEmployee(Mary Brown, 3333, 500.0, 110.0);paymentSystem.addPayee(maryBrown);SalaryEmployee susanWhite new SalaryEmployee(Susan White, 4444, 470.0, 130.0);paymentSystem.addPayee(susanWhite);ContractingCompany acmeInc new ContractingCompany(Acme Inc, 5555);paymentSystem.addPayee(acmeInc);ContractingCompany javaCodeGeeks new ContractingCompany(javacodegeeks.com, 6666);paymentSystem.addPayee(javaCodeGeeks);// Simulate WeekjohnSmith.giveCommission(40.0);johnSmith.giveCommission(35.0);johnSmith.giveCommission(45.0);johnSmith.giveBonus(5.0);paulJones.giveCommission(45.0);paulJones.giveCommission(51.0);paulJones.giveCommission(23.0);paulJones.giveCommission(14.5);paulJones.giveCommission(57.3);paulJones.giveBonus(6.5);maryBrown.giveBonus(3.0);susanWhite.giveBonus(7.5);acmeInc.payForServices(100.0);acmeInc.payForServices(250.0);acmeInc.payForServices(300.0);javaCodeGeeks.payForServices(400.0);javaCodeGeeks.payForServices(250.0);// Process Weekly PaymentpaymentSystem.processPayments();}
} 输出 Paying to John SmithGross 442.5Tax -68.5Net 374.0Transferred to Account: 1111
Paying to Paul JonesGross 574.925Tax -89.985Net 484.93999999999994Transferred to Account: 2222
Paying to Mary BrownGross 515.0Tax -81.0Net 434.0Transferred to Account: 3333
Paying to Susan WhiteGross 505.25Tax -75.05Net 430.2Transferred to Account: 4444
Paying to Acme IncGross 650.0Tax -0.0Net 650.0Transferred to Account: 5555
Paying to javacodegeeks.comGross 650.0Tax -0.0Net 650.0Transferred to Account: 6666 如您所见税收是为员工计算和预扣的而签约公司继续不交税。 老板不能对这个系统感到满意而是要给我们一个带薪休假的全部费用以使他免受监禁 5.结论 如果以正确的方式使用抽象它是Java中非常强大的工具。 它为高级Java使用以及构建复杂可扩展和可维护的软件打开了许多可能性。 我们只是简单地了解了抽象可以为我们做些什么的表面并希望为了解可以更详细地使用抽象的不同方式奠定基础。 翻译自: https://www.javacodegeeks.com/2014/07/abstraction-in-java.html