'深入研究多態性及其在Java中的優勢'

"

多態性是面向對象軟件的基本原則之一。該術語通常表示可以具有多種形式的東西。在面向對象的方法中,多態性使得編寫具有後期綁定引用的程序成為可能。雖然在Java中創建多態引用很容易,但它背後的概念對整體編程有更深的影響。本文探討了一些關於多態性的複雜細節及其對面向對象編程的意義。

概述

多態性參考是可以指的是不同類型的對象的在不同的時間點的變量。它通常與它所引用的類兼容。例如,在以下情況中:

Employee employee;

' employee '是一個引用變量,可以引用Employee的一個實例類。引用變量對引用對象的限定由其兼容性決定。這似乎是唯一可靠的條件,但這並不完全正確,尤其是在實現多態時。規則過於嚴格,但多態性通過結合“有多種形式”的思想而變得更加靈活。這意味著多態引用可以保證它可以在不同的時間點引用不同類型的對象,而不是堅持完全匹配兼容性的想法。因此,如果可以使用引用在某個時間點調用方法,則可以動態地將其更改為指向另一個對象,並在下次調用其他一些方法。這通過給出參考變量的另一個使用維度來利用靈活性。

當引用變量綁定到在運行時無法更改的對象時,或者換句話說,方法調用與方法定義的綁定在編譯時完成,它稱為靜態綁定。如果綁定在運行時是可更改的,就像多態引用的情況一樣,只在執行期間進行綁定的決定,它被稱為動態綁定後期綁定。兩者都有用於面向對象的編程,並不是一個比另一個更重要。但是,在多態引用的情況下,綁定的延遲承諾使其在靈活性方面優於編譯時綁定,但另一方面,在性能開銷方面有所妥協。但是,這在很大程度上是可以接受的,並且在提高效率方面,開銷通常具有非常小的吸引力。

創造多態性

在Java中,可以通過兩種方式創建多態引用:使用繼承或使用接口

遺傳多態性

參考變量是指一類的一個實例。在繼承層次結構的情況下,如果引用變量在分層樹中聲明為父類類型,則引用對象可以指向層次結構中任何類的實例。這意味著,在Java中,因為Object類是所有類的父類或超類,或者換句話說,Java中的所有類實際上是隱式或顯式的Object類的子類,引用變量是在對象類型可以指任何的Java類的實例。這就是我們的意思。

Employee employee;
Object object;
employee = new Employee();
object = employee; // 這是一個有效的作業

如果情況發生逆轉,如下:

Employee employee;
Object object = new Object();
employee = (Employee)object // 有效,但需要顯式強制轉換

觀察它需要一個明確的演員表; 只有這樣它才成為有效的陳述。可以看出,這種反向分配對於在許多情況下存在問題的邊緣不太有用。這是因為Object實例的功能與Employee引用變量所期望的功能幾乎沒有關係。關係是-a可以從employee-is-an-object衍生物中導出; 在這種情況下的反向關係,例如,一個對象是一個員工是太牽強了。

遺傳多態性:一個例子

讓我們試著通過一個例子來理解它。

"

多態性是面向對象軟件的基本原則之一。該術語通常表示可以具有多種形式的東西。在面向對象的方法中,多態性使得編寫具有後期綁定引用的程序成為可能。雖然在Java中創建多態引用很容易,但它背後的概念對整體編程有更深的影響。本文探討了一些關於多態性的複雜細節及其對面向對象編程的意義。

概述

多態性參考是可以指的是不同類型的對象的在不同的時間點的變量。它通常與它所引用的類兼容。例如,在以下情況中:

Employee employee;

' employee '是一個引用變量,可以引用Employee的一個實例類。引用變量對引用對象的限定由其兼容性決定。這似乎是唯一可靠的條件,但這並不完全正確,尤其是在實現多態時。規則過於嚴格,但多態性通過結合“有多種形式”的思想而變得更加靈活。這意味著多態引用可以保證它可以在不同的時間點引用不同類型的對象,而不是堅持完全匹配兼容性的想法。因此,如果可以使用引用在某個時間點調用方法,則可以動態地將其更改為指向另一個對象,並在下次調用其他一些方法。這通過給出參考變量的另一個使用維度來利用靈活性。

當引用變量綁定到在運行時無法更改的對象時,或者換句話說,方法調用與方法定義的綁定在編譯時完成,它稱為靜態綁定。如果綁定在運行時是可更改的,就像多態引用的情況一樣,只在執行期間進行綁定的決定,它被稱為動態綁定後期綁定。兩者都有用於面向對象的編程,並不是一個比另一個更重要。但是,在多態引用的情況下,綁定的延遲承諾使其在靈活性方面優於編譯時綁定,但另一方面,在性能開銷方面有所妥協。但是,這在很大程度上是可以接受的,並且在提高效率方面,開銷通常具有非常小的吸引力。

創造多態性

在Java中,可以通過兩種方式創建多態引用:使用繼承或使用接口

遺傳多態性

參考變量是指一類的一個實例。在繼承層次結構的情況下,如果引用變量在分層樹中聲明為父類類型,則引用對象可以指向層次結構中任何類的實例。這意味著,在Java中,因為Object類是所有類的父類或超類,或者換句話說,Java中的所有類實際上是隱式或顯式的Object類的子類,引用變量是在對象類型可以指任何的Java類的實例。這就是我們的意思。

Employee employee;
Object object;
employee = new Employee();
object = employee; // 這是一個有效的作業

如果情況發生逆轉,如下:

Employee employee;
Object object = new Object();
employee = (Employee)object // 有效,但需要顯式強制轉換

觀察它需要一個明確的演員表; 只有這樣它才成為有效的陳述。可以看出,這種反向分配對於在許多情況下存在問題的邊緣不太有用。這是因為Object實例的功能與Employee引用變量所期望的功能幾乎沒有關係。關係是-a可以從employee-is-an-object衍生物中導出; 在這種情況下的反向關係,例如,一個對象是一個員工是太牽強了。

遺傳多態性:一個例子

讓我們試著通過一個例子來理解它。

深入研究多態性及其在Java中的優勢

名為Company的驅動程序類創建一個僱員列表並調用paySalary()方法。該工資類維護不同類型的公司員工名單。請注意,該數組被聲明為派生自Employee類的引用變量數組,該類是所有僱員子類的父類或超類。因此,可以使用Employee類的任何子類(例如CommissionEmployeeHourlyEmployeeSalariedEmployee)創建的對象引用來填充數組。在paySalary()定義中,相應的salary()根據數組中的對象引用調用方法。因此,對salary()方法的調用是多態的,很明顯,每個類都有自己的salary()方法版本。

Payroll類中的employees數組不代表特定類型的Employee。它作為一個句柄,可以指向任何類型的Employee子類引用。雖然繼承的類共享一些公共數據,作為後代繼承,但它們與它們自己的屬性集是不同的。

遺傳多態:Java實現

以下是Java中示例的快速實現。

package org.mano.example;
public class Company
{
public static void main( String[] args )
{
Payroll payroll = new Payroll();
payroll.paySalary();
}
}
package org.mano.example;
import java.util.ArrayList;
import java.util.List;
public class Payroll {
private List<Employee> employees =
new ArrayList<>();
public Payroll() {
employees.add(new
SalariedEmployee("Harry Potter",
"123-234-345",7800));
employees.add(new
CommissionEmployee("Peter Parker",
"234-345-456",2345.67,0.15));
employees.add(new
HourlyEmployee("Joker Poker",
"456-567-678",562.36,239.88));
}
public void paySalary() {
for (Employee e: employees) {
System.out.println
("----------------------------------------------------");
System.out.println(e.toString());
System.out.printf
("Gross payment: $%,.2f\\n",e.salary());
System.out.println
("----------------------------------------------------");
}
}
}
package org.mano.example;
public abstract class Employee {
protected String name;
protected String ssn;
public Employee(String name, String ssn) {
this.name = name;
this.ssn = ssn;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSsn() {
return ssn;
}
public void setSsn(String ssn) {
this.ssn = ssn;
}
@Override
public String toString() {
return String.format("%s\\nSSN: %s",
getName(),getSsn());
}
public abstract double salary();
}
package org.mano.example;
public class SalariedEmployee extends Employee {
protected double basicSalary;
public SalariedEmployee(String name, String ssn,
double basicSalary) {
super(name, ssn);
setBasicSalary(basicSalary);
}
public double getBasicSalary() {
return basicSalary;
}
public void setBasicSalary(double basicSalary) {
if(basicSalary>= 0.0)
this.basicSalary = basicSalary;
else
throw new IllegalArgumentException("basic " +
"salary must be greater than 0.0");
}
@Override
public double salary() {
eturn getBasicSalary();
}
@Override
public String toString() {
return String.format("%s\\nBasic Salary: $%,.2f",
super.toString(),getBasicSalary());
}
}
package org.mano.example;
public class HourlyEmployee extends Employee {
protected double wage;
protected double hours;
public HourlyEmployee(String name, String ssn,
double wage, double hours) {
super (name, ssn);
setWage(wage);
setHours(hours);
}
public double getWage() {
return wage;
}
public void setWage(double wage) {
if(wage >= 0.0)
this.wage = wage;
else
throw new IllegalArgumentException("wage " +
"must be > 0.0");
}
public double getHours() {
return hours;
}
public void setHours(double hours) {
if(hours >= 0.0)
this.hours = hours;
else
throw new IllegalArgumentException("hours " +
"must be > 0.0");
}
@Override
public double salary() {
return getHours() * getWage();
}
@Override
public String toString() {
return String.format("%s\\nWage: $%,
.2f\\nHours worked: %,.2f",
super.toString(),getWage(),getHours());
}
}
package org.mano.example;
public class CommissionEmployee extends Employee {
protected double sales;
protected double commission;
public CommissionEmployee(String name, String ssn,
double sales, double commission) {
super(name, ssn);
setSales(sales);
setCommission(commission);
}
public double getSales() {
return sales;
}
public void setSales(double sales) {
if(sales >=0.0)
this.sales = sales;
else
throw new IllegalArgumentException("Sales " +
"must be >= 0.0");
}
public double getCommission() {
return commission;
}
public void setCommission(double commission) {
if(commission > 0.0 && commission < 1.0)
this.commission = commission;
else
throw new IllegalArgumentException("Commission " +
"must be between 0.0 and 1.0");
}
@Override
public double salary() {
return getCommission() * getSales();
}
@Override
public String toString() {
return String.format("%s\\nSales: %,
.2f\\nCommission: %,.2f",
super.toString(),getSales(),getCommission());
}
}

接口的多態性

接口的多態性與前面的示例非常相似,除了這裡的多態規則是根據Java接口指定的規範。接口名稱可以用作引用變量,就像我們上面的類名一樣。它指的是任何實現接口的類的任何對象。這是一個例子。

package org.mano.example;
public interface Player {
public enum STATUS{PLAY,PAUSE,STOP};
public void play();
public void stop();
public void pause();
}
package org.mano.example;
public class VideoPlayer implements Player {
private STATUS currentStatus = STATUS.STOP;
@Override
public void play() {
if(currentStatus == STATUS.STOP ||
currentStatus == STATUS.PAUSE) {
currentStatus = STATUS.PLAY;
System.out.println("Playing Video...");
}
else
System.out.println("I am ON playing man!");
}
@Override
public voidstop() {
if(currentStatus == STATUS.PLAY ||
currentStatus == STATUS.PAUSE) {
currentStatus = STATUS.STOP;
System.out.println("Video play stopped.");
}
else
System.out.println("Do you want me to go fishing?");
}
@Override
public void pause() {
if(currentStatus == STATUS.PLAY) {
currentStatus = STATUS.PAUSE;
System.out.println("Video play paused.");
}
else
System.out.println("I'm a statue. You froze me
already!");
}
}
package org.mano.example;
public class AudioPlayer implements Player {
private STATUS currentStatus = STATUS.STOP;
@Override
public void play() {
if(currentStatus == STATUS.STOP ||
currentStatus == STATUS.PAUSE) {
currentStatus = STATUS.PLAY;
System.out.println("Playing Audio...");
}
else
System.out.println("I am ON playing man!");
}
@Override
public void stop() {
if(currentStatus == STATUS.PLAY ||
currentStatus == STATUS.PAUSE) {
currentStatus = STATUS.STOP;
System.out.println("Audio play stopped.");
}
else
System.out.println("Do you want me to go fishing?");
}
@Override
public void pause() {
if(currentStatus == STATUS.PLAY) {
currentStatus = STATUS.PAUSE;
System.out.println("Audio play paused.");
}
else
System.out.println("I'm a statue. You froze me
already!");
}
}
package org.mano.example;
public class PlayerApp {
public static void main(String[] args) {
Player player= new VideoPlayer();
player.play();
player.pause();
player.stop();
player= new AudioPlayer();
player.play();
player.pause();
player.stop();
}
}

請注意,在PlayerApp中,我們使用接口Player來聲明對象引用變量。引用變量播放器可以引用任何實現Player接口的類的任何對象。為了證明這一點,我們在這裡使用了相同的播放器變量來引用VideoPlayer對象和AudioPlayer對象。在運行時調用的方法是特定於它引用的類對象的方法。實現接口的類和接口本身之間的關係是父和子,正如我們在帶有繼承的多態性的例子中看到的那樣。它也是一個 關係並形成多態性的基礎。

結論

通過類繼承或通過接口實現多態性之間的區別是一個選擇問題。實際上,區別在於理解類和接口的屬性和特性。除非通過了解其性質,否則沒有嚴格的規則來定義什麼時候使用。這超出了本文的範圍。但是,在多態性中,這兩個想法都適合我們想做的事情。就這樣。

整理不易,請大家多多評論轉發加收藏,您的支持是我最大的動力。

"

多態性是面向對象軟件的基本原則之一。該術語通常表示可以具有多種形式的東西。在面向對象的方法中,多態性使得編寫具有後期綁定引用的程序成為可能。雖然在Java中創建多態引用很容易,但它背後的概念對整體編程有更深的影響。本文探討了一些關於多態性的複雜細節及其對面向對象編程的意義。

概述

多態性參考是可以指的是不同類型的對象的在不同的時間點的變量。它通常與它所引用的類兼容。例如,在以下情況中:

Employee employee;

' employee '是一個引用變量,可以引用Employee的一個實例類。引用變量對引用對象的限定由其兼容性決定。這似乎是唯一可靠的條件,但這並不完全正確,尤其是在實現多態時。規則過於嚴格,但多態性通過結合“有多種形式”的思想而變得更加靈活。這意味著多態引用可以保證它可以在不同的時間點引用不同類型的對象,而不是堅持完全匹配兼容性的想法。因此,如果可以使用引用在某個時間點調用方法,則可以動態地將其更改為指向另一個對象,並在下次調用其他一些方法。這通過給出參考變量的另一個使用維度來利用靈活性。

當引用變量綁定到在運行時無法更改的對象時,或者換句話說,方法調用與方法定義的綁定在編譯時完成,它稱為靜態綁定。如果綁定在運行時是可更改的,就像多態引用的情況一樣,只在執行期間進行綁定的決定,它被稱為動態綁定後期綁定。兩者都有用於面向對象的編程,並不是一個比另一個更重要。但是,在多態引用的情況下,綁定的延遲承諾使其在靈活性方面優於編譯時綁定,但另一方面,在性能開銷方面有所妥協。但是,這在很大程度上是可以接受的,並且在提高效率方面,開銷通常具有非常小的吸引力。

創造多態性

在Java中,可以通過兩種方式創建多態引用:使用繼承或使用接口

遺傳多態性

參考變量是指一類的一個實例。在繼承層次結構的情況下,如果引用變量在分層樹中聲明為父類類型,則引用對象可以指向層次結構中任何類的實例。這意味著,在Java中,因為Object類是所有類的父類或超類,或者換句話說,Java中的所有類實際上是隱式或顯式的Object類的子類,引用變量是在對象類型可以指任何的Java類的實例。這就是我們的意思。

Employee employee;
Object object;
employee = new Employee();
object = employee; // 這是一個有效的作業

如果情況發生逆轉,如下:

Employee employee;
Object object = new Object();
employee = (Employee)object // 有效,但需要顯式強制轉換

觀察它需要一個明確的演員表; 只有這樣它才成為有效的陳述。可以看出,這種反向分配對於在許多情況下存在問題的邊緣不太有用。這是因為Object實例的功能與Employee引用變量所期望的功能幾乎沒有關係。關係是-a可以從employee-is-an-object衍生物中導出; 在這種情況下的反向關係,例如,一個對象是一個員工是太牽強了。

遺傳多態性:一個例子

讓我們試著通過一個例子來理解它。

深入研究多態性及其在Java中的優勢

名為Company的驅動程序類創建一個僱員列表並調用paySalary()方法。該工資類維護不同類型的公司員工名單。請注意,該數組被聲明為派生自Employee類的引用變量數組,該類是所有僱員子類的父類或超類。因此,可以使用Employee類的任何子類(例如CommissionEmployeeHourlyEmployeeSalariedEmployee)創建的對象引用來填充數組。在paySalary()定義中,相應的salary()根據數組中的對象引用調用方法。因此,對salary()方法的調用是多態的,很明顯,每個類都有自己的salary()方法版本。

Payroll類中的employees數組不代表特定類型的Employee。它作為一個句柄,可以指向任何類型的Employee子類引用。雖然繼承的類共享一些公共數據,作為後代繼承,但它們與它們自己的屬性集是不同的。

遺傳多態:Java實現

以下是Java中示例的快速實現。

package org.mano.example;
public class Company
{
public static void main( String[] args )
{
Payroll payroll = new Payroll();
payroll.paySalary();
}
}
package org.mano.example;
import java.util.ArrayList;
import java.util.List;
public class Payroll {
private List<Employee> employees =
new ArrayList<>();
public Payroll() {
employees.add(new
SalariedEmployee("Harry Potter",
"123-234-345",7800));
employees.add(new
CommissionEmployee("Peter Parker",
"234-345-456",2345.67,0.15));
employees.add(new
HourlyEmployee("Joker Poker",
"456-567-678",562.36,239.88));
}
public void paySalary() {
for (Employee e: employees) {
System.out.println
("----------------------------------------------------");
System.out.println(e.toString());
System.out.printf
("Gross payment: $%,.2f\\n",e.salary());
System.out.println
("----------------------------------------------------");
}
}
}
package org.mano.example;
public abstract class Employee {
protected String name;
protected String ssn;
public Employee(String name, String ssn) {
this.name = name;
this.ssn = ssn;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSsn() {
return ssn;
}
public void setSsn(String ssn) {
this.ssn = ssn;
}
@Override
public String toString() {
return String.format("%s\\nSSN: %s",
getName(),getSsn());
}
public abstract double salary();
}
package org.mano.example;
public class SalariedEmployee extends Employee {
protected double basicSalary;
public SalariedEmployee(String name, String ssn,
double basicSalary) {
super(name, ssn);
setBasicSalary(basicSalary);
}
public double getBasicSalary() {
return basicSalary;
}
public void setBasicSalary(double basicSalary) {
if(basicSalary>= 0.0)
this.basicSalary = basicSalary;
else
throw new IllegalArgumentException("basic " +
"salary must be greater than 0.0");
}
@Override
public double salary() {
eturn getBasicSalary();
}
@Override
public String toString() {
return String.format("%s\\nBasic Salary: $%,.2f",
super.toString(),getBasicSalary());
}
}
package org.mano.example;
public class HourlyEmployee extends Employee {
protected double wage;
protected double hours;
public HourlyEmployee(String name, String ssn,
double wage, double hours) {
super (name, ssn);
setWage(wage);
setHours(hours);
}
public double getWage() {
return wage;
}
public void setWage(double wage) {
if(wage >= 0.0)
this.wage = wage;
else
throw new IllegalArgumentException("wage " +
"must be > 0.0");
}
public double getHours() {
return hours;
}
public void setHours(double hours) {
if(hours >= 0.0)
this.hours = hours;
else
throw new IllegalArgumentException("hours " +
"must be > 0.0");
}
@Override
public double salary() {
return getHours() * getWage();
}
@Override
public String toString() {
return String.format("%s\\nWage: $%,
.2f\\nHours worked: %,.2f",
super.toString(),getWage(),getHours());
}
}
package org.mano.example;
public class CommissionEmployee extends Employee {
protected double sales;
protected double commission;
public CommissionEmployee(String name, String ssn,
double sales, double commission) {
super(name, ssn);
setSales(sales);
setCommission(commission);
}
public double getSales() {
return sales;
}
public void setSales(double sales) {
if(sales >=0.0)
this.sales = sales;
else
throw new IllegalArgumentException("Sales " +
"must be >= 0.0");
}
public double getCommission() {
return commission;
}
public void setCommission(double commission) {
if(commission > 0.0 && commission < 1.0)
this.commission = commission;
else
throw new IllegalArgumentException("Commission " +
"must be between 0.0 and 1.0");
}
@Override
public double salary() {
return getCommission() * getSales();
}
@Override
public String toString() {
return String.format("%s\\nSales: %,
.2f\\nCommission: %,.2f",
super.toString(),getSales(),getCommission());
}
}

接口的多態性

接口的多態性與前面的示例非常相似,除了這裡的多態規則是根據Java接口指定的規範。接口名稱可以用作引用變量,就像我們上面的類名一樣。它指的是任何實現接口的類的任何對象。這是一個例子。

package org.mano.example;
public interface Player {
public enum STATUS{PLAY,PAUSE,STOP};
public void play();
public void stop();
public void pause();
}
package org.mano.example;
public class VideoPlayer implements Player {
private STATUS currentStatus = STATUS.STOP;
@Override
public void play() {
if(currentStatus == STATUS.STOP ||
currentStatus == STATUS.PAUSE) {
currentStatus = STATUS.PLAY;
System.out.println("Playing Video...");
}
else
System.out.println("I am ON playing man!");
}
@Override
public voidstop() {
if(currentStatus == STATUS.PLAY ||
currentStatus == STATUS.PAUSE) {
currentStatus = STATUS.STOP;
System.out.println("Video play stopped.");
}
else
System.out.println("Do you want me to go fishing?");
}
@Override
public void pause() {
if(currentStatus == STATUS.PLAY) {
currentStatus = STATUS.PAUSE;
System.out.println("Video play paused.");
}
else
System.out.println("I'm a statue. You froze me
already!");
}
}
package org.mano.example;
public class AudioPlayer implements Player {
private STATUS currentStatus = STATUS.STOP;
@Override
public void play() {
if(currentStatus == STATUS.STOP ||
currentStatus == STATUS.PAUSE) {
currentStatus = STATUS.PLAY;
System.out.println("Playing Audio...");
}
else
System.out.println("I am ON playing man!");
}
@Override
public void stop() {
if(currentStatus == STATUS.PLAY ||
currentStatus == STATUS.PAUSE) {
currentStatus = STATUS.STOP;
System.out.println("Audio play stopped.");
}
else
System.out.println("Do you want me to go fishing?");
}
@Override
public void pause() {
if(currentStatus == STATUS.PLAY) {
currentStatus = STATUS.PAUSE;
System.out.println("Audio play paused.");
}
else
System.out.println("I'm a statue. You froze me
already!");
}
}
package org.mano.example;
public class PlayerApp {
public static void main(String[] args) {
Player player= new VideoPlayer();
player.play();
player.pause();
player.stop();
player= new AudioPlayer();
player.play();
player.pause();
player.stop();
}
}

請注意,在PlayerApp中,我們使用接口Player來聲明對象引用變量。引用變量播放器可以引用任何實現Player接口的類的任何對象。為了證明這一點,我們在這裡使用了相同的播放器變量來引用VideoPlayer對象和AudioPlayer對象。在運行時調用的方法是特定於它引用的類對象的方法。實現接口的類和接口本身之間的關係是父和子,正如我們在帶有繼承的多態性的例子中看到的那樣。它也是一個 關係並形成多態性的基礎。

結論

通過類繼承或通過接口實現多態性之間的區別是一個選擇問題。實際上,區別在於理解類和接口的屬性和特性。除非通過了解其性質,否則沒有嚴格的規則來定義什麼時候使用。這超出了本文的範圍。但是,在多態性中,這兩個想法都適合我們想做的事情。就這樣。

整理不易,請大家多多評論轉發加收藏,您的支持是我最大的動力。

深入研究多態性及其在Java中的優勢

"

相關推薦

推薦中...