ใครช่วยยกตัวอย่างง่ายๆที่อธิบายความแตกต่างระหว่างDynamicและStatic polymorphism ใน Java
ใครช่วยยกตัวอย่างง่ายๆที่อธิบายความแตกต่างระหว่างDynamicและStatic polymorphism ใน Java
คำตอบ:
ความแตกต่าง
1. การผูกแบบคงที่ / การรวมเวลาการผูก / การผูกมัดในช่วงต้น / การโอเวอร์โหลดวิธีการ (ในคลาสเดียวกัน)
2. การผูกแบบไดนามิก / การผูกเวลารัน / การผูกล่าช้า / การแทนที่วิธีการ (ในคลาสที่แตกต่างกัน)
class Calculation {
void sum(int a,int b){System.out.println(a+b);}
void sum(int a,int b,int c){System.out.println(a+b+c);}
public static void main(String args[]) {
Calculation obj=new Calculation();
obj.sum(10,10,10); // 30
obj.sum(20,20); //40
}
}
class Animal {
public void move(){
System.out.println("Animals can move");
}
}
class Dog extends Animal {
public void move() {
System.out.println("Dogs can walk and run");
}
}
public class TestDog {
public static void main(String args[]) {
Animal a = new Animal(); // Animal reference and object
Animal b = new Dog(); // Animal reference but Dog object
a.move();//output: Animals can move
b.move();//output:Dogs can walk and run
}
}
Animal reference but Dog object
ทำไมเราถึงใช้Dog reference and dog object
ไม่ได้
วิธีการโอเวอร์โหลดจะเป็นตัวอย่างของความหลากหลายแบบคงที่
ในขณะที่การลบล้างจะเป็นตัวอย่างของความหลากหลายแบบไดนามิก
เนื่องจากในกรณีที่มีการโอเวอร์โหลดในเวลาคอมไพล์คอมไพเลอร์จะรู้ว่าวิธีใดที่จะเชื่อมโยงกับการเรียกใช้ อย่างไรก็ตามมันถูกกำหนดที่รันไทม์สำหรับความหลากหลายแบบไดนามิก
ความหลากหลายแบบไดนามิก (เวลาทำงาน)คือความหลากหลายที่มีอยู่ในเวลาทำงาน ที่นี่คอมไพเลอร์ Java ไม่เข้าใจว่าเมธอดใดถูกเรียกในเวลาคอมไพล์ JVM เท่านั้นที่ตัดสินใจว่าจะเรียกเมธอดใดในขณะรันไทม์ การโอเวอร์โหลดเมธอดและการแทนที่เมธอดโดยใช้วิธีอินสแตนซ์เป็นตัวอย่างของความหลากหลายแบบไดนามิก
ตัวอย่างเช่น,
พิจารณาแอปพลิเคชันที่ทำให้เป็นอนุกรมและแยกลำดับของเอกสารประเภทต่างๆ
เราสามารถมี 'เอกสาร' เป็นคลาสพื้นฐานและคลาสประเภทเอกสารต่างๆที่ได้มาจากมัน เช่น XMLDocument, WordDocument เป็นต้น
คลาสเอกสารจะกำหนดเมธอด 'Serialize ()' และ 'De-serialize ()' เป็นเสมือนและคลาสที่ได้รับแต่ละคลาสจะใช้วิธีการเหล่านี้ในแบบของตัวเองตามเนื้อหาจริงของเอกสาร
เมื่อเอกสารประเภทต่างๆจำเป็นต้องทำให้เป็นซีเรียล / ไม่เป็นซีเรียลอ็อบเจ็กต์เอกสารจะถูกอ้างอิงโดยการอ้างอิงคลาส 'เอกสาร' (หรือตัวชี้) และเมื่อมีการเรียกเมธอด 'Serialize ()' หรือ 'De-serialize ()' ในนั้นจะมีการเรียกวิธีการเสมือนเวอร์ชันที่เหมาะสม
ความหลากหลายแบบคงที่ (เวลาคอมไพล์)คือความหลากหลายที่แสดงในเวลาคอมไพล์ ที่นี่คอมไพเลอร์ Java รู้ว่าวิธีใดเรียกว่า วิธีการโอเวอร์โหลดและวิธีการแทนที่โดยใช้วิธีคงที่ วิธีการแทนที่โดยใช้วิธีส่วนตัวหรือขั้นสุดท้ายเป็นตัวอย่างของความหลากหลายแบบคงที่
ตัวอย่างเช่น,
อ็อบเจ็กต์พนักงานอาจมีสองวิธีการพิมพ์ () วิธีหนึ่งโดยไม่มีข้อโต้แย้งและอีกวิธีหนึ่งใช้สตริงคำนำหน้าเพื่อแสดงพร้อมกับข้อมูลพนักงาน
ด้วยอินเทอร์เฟซเหล่านี้เมื่อเมธอด print () ถูกเรียกโดยไม่มีอาร์กิวเมนต์ใด ๆ คอมไพเลอร์เมื่อมองไปที่อาร์กิวเมนต์ของฟังก์ชันจะรู้ว่าฟังก์ชันใดถูกเรียกใช้และสร้างโค้ดอ็อบเจ็กต์ตามนั้น
สำหรับรายละเอียดเพิ่มเติมโปรดอ่าน "Polymorphism คืออะไร" (Google it)
การผูกหมายถึงการเชื่อมโยงระหว่างการเรียกใช้วิธีการและการกำหนดวิธีการ
ภาพนี้แสดงให้เห็นชัดเจนว่ามีผลผูกพันอะไร
ในภาพนี้การเรียก "a1.methodOne ()" มีผลผูกพันกับนิยาม methodOne () ที่สอดคล้องกันและการเรียก "a1.methodTwo ()" มีผลผูกพันกับนิยาม methodTwo () ที่เกี่ยวข้อง
สำหรับการเรียกทุกวิธีควรมีการกำหนดวิธีการที่เหมาะสม นี่เป็นกฎใน java หากคอมไพลเลอร์ไม่เห็นนิยามเมธอดที่เหมาะสมสำหรับการเรียกทุกเมธอดคอมไพเลอร์จะแสดงข้อผิดพลาด
ตอนนี้มาที่การรวมแบบคงที่และการผูกแบบไดนามิกใน java
การผูกแบบคงที่ใน Java:
การผูกแบบคงที่คือการผูกที่เกิดขึ้นระหว่างการคอมไพล์ เรียกอีกอย่างว่าการโยงก่อนเนื่องจากการผูกเกิดขึ้นก่อนที่โปรแกรมจะทำงานจริง
.
การผูกแบบคงที่สามารถแสดงได้ดังภาพด้านล่าง
ในภาพนี้ 'a1' เป็นตัวแปรอ้างอิงประเภทคลาส A ที่ชี้ไปยังออบเจ็กต์ของคลาส A 'a2' ยังเป็นตัวแปรอ้างอิงของคลาส A แต่ชี้ไปที่ออบเจ็กต์ของคลาส B
ในระหว่างการคอมไพล์ในขณะที่โยงคอมไพลเลอร์จะไม่ตรวจสอบชนิดของอ็อบเจ็กต์ที่ตัวแปรอ้างอิงเฉพาะชี้ไป เพียงแค่ตรวจสอบชนิดของตัวแปรอ้างอิงที่เรียกใช้เมธอดและตรวจสอบว่ามีนิยามวิธีการสำหรับมันในประเภทนั้นหรือไม่
ตัวอย่างเช่นสำหรับการเรียกเมธอด“ a1.method ()” ในภาพด้านบนคอมไพเลอร์จะตรวจสอบว่ามีการกำหนดวิธีการสำหรับ method () ในคลาส A หรือไม่เนื่องจาก 'a1′ เป็นประเภทคลาส A ในทำนองเดียวกันสำหรับการเรียกเมธอด“ a2.method ()” จะตรวจสอบว่ามีนิยามวิธีการสำหรับ method () ในคลาส A หรือไม่เนื่องจาก 'a2′ เป็นประเภทคลาส A ด้วย ไม่ได้ตรวจสอบว่าวัตถุใดชี้ 'a1' และ 'a2' การผูกแบบนี้เรียกว่าการผูกแบบคงที่
การผูกแบบไดนามิกใน Java:
การเชื่อมโยงแบบไดนามิกคือการเชื่อมโยงที่เกิดขึ้นระหว่างเวลาทำงาน เรียกอีกอย่างว่าการโยงสายเนื่องจากการผูกจะเกิดขึ้นเมื่อโปรแกรมกำลังทำงานจริง
ในระหว่างรันไทม์อ็อบเจ็กต์จริงถูกใช้สำหรับการโยง ตัวอย่างเช่นสำหรับการเรียก“ a1.method ()” ในภาพด้านบนจะมีการเรียก method () ของวัตถุจริงที่ชี้ไปที่ 'a1' สำหรับการเรียก“ a2.method ()” จะมีการเรียก method () ของวัตถุจริงที่ชี้ไปที่ 'a2' การผูกแบบนี้เรียกว่าการผูกแบบไดนามิก
การเชื่อมโยงแบบไดนามิกของตัวอย่างข้างต้นสามารถแสดงได้ดังต่อไปนี้
Polymorphism: ความ หลากหลายเป็นความสามารถของวัตถุในหลายรูปแบบ การใช้ความหลากหลายของความหลากหลายใน OOP เกิดขึ้นเมื่อการอ้างอิงคลาสพาเรนต์ถูกใช้เพื่ออ้างถึงอ็อบเจ็กต์คลาสลูก
Dynamic Binding / Runtime Polymorphism:
เวลาเรียกใช้ Polymorphism หรือที่เรียกว่า method overriding ในกลไกนี้ซึ่งการเรียกไปยังฟังก์ชันที่ถูกแทนที่จะได้รับการแก้ไขในเวลาทำงาน
public class DynamicBindingTest {
public static void main(String args[]) {
Vehicle vehicle = new Car(); //here Type is vehicle but object will be Car
vehicle.start(); //Car's start called because start() is overridden method
}
}
class Vehicle {
public void start() {
System.out.println("Inside start method of Vehicle");
}
}
class Car extends Vehicle {
@Override
public void start() {
System.out.println("Inside start method of Car");
}
}
เอาท์พุท:
วิธีสตาร์ทภายในรถ
ความหลากหลายของการผูกแบบคงที่ / เวลาคอมไพล์:
วิธีการที่จะเรียกนั้นจะตัดสินใจในเวลาคอมไพล์เท่านั้น
public class StaticBindingTest {
public static void main(String args[]) {
Collection c = new HashSet();
StaticBindingTest et = new StaticBindingTest();
et.sort(c);
}
//overloaded method takes Collection argument
public Collection sort(Collection c){
System.out.println("Inside Collection sort method");
return c;
}
//another overloaded method which takes HashSet argument which is sub class
public Collection sort(HashSet hs){
System.out.println("Inside HashSet sort method");
return hs;
}
}
เอาท์พุท: Inside Collection sort
method overloadingเป็นตัวอย่างของ compile time / static polymorphism เนื่องจากวิธีการผูกระหว่าง method call และ method definition เกิดขึ้นที่ compile time และขึ้นอยู่กับ reference ของ class (การอ้างอิงที่สร้างใน compile time และไปที่ stack)
method overridingเป็นตัวอย่างของ run time / dynamic polymorphism เนื่องจากวิธีการผูกระหว่าง method call และ method definition เกิดขึ้นที่ run time และขึ้นอยู่กับ object ของ class (object ที่สร้างขึ้นที่ runtime และไปที่ heap)
พูดง่ายๆคือ
ความแตกต่างแบบคงที่ : ชื่อเมธอดเดียวกันมีมากเกินไปโดยมีประเภทหรือจำนวนพารามิเตอร์ที่แตกต่างกันในคลาสเดียวกัน (ลายเซ็นต่างกัน) การเรียกเมธอดเป้าหมายได้รับการแก้ไขในเวลาคอมไพล์
ความหลากหลายแบบไดนามิก : เมธอดเดียวกันถูกแทนที่ด้วยลายเซ็นเดียวกันในคลาสต่างๆ ไม่ทราบชนิดของอ็อบเจ็กต์ที่จะเรียกใช้เมธอดในขณะคอมไพล์ แต่จะถูกกำหนดในขณะรันไทม์
โดยทั่วไปแล้วการโอเวอร์โหลดจะไม่ถูกพิจารณาว่าเป็นความหลากหลาย
จากหน้าสอน Java :
คลาสย่อยของคลาสสามารถกำหนดพฤติกรรมเฉพาะของตนเองและยังแชร์ฟังก์ชันการทำงานบางอย่างของคลาสพาเรนต์ได้
Generally overloading won't be considered as polymorphism.
คุณช่วยอธิบายอย่างละเอียดในประเด็นนี้ได้ไหม
Method Overloadingเรียกว่าStatic Polymorphismและเรียกอีกอย่างว่าCompile Time PolymorphismหรือStatic Bindingเนื่องจากการเรียกเมธอดที่โอเวอร์โหลดจะได้รับการแก้ไขในเวลาคอมไพล์โดยคอมไพเลอร์บนพื้นฐานของรายการอาร์กิวเมนต์และการอ้างอิงที่เราเรียกใช้เมธอด
และMethod Overridingเรียกว่าDynamic PolymorphismหรือPolymorphismหรือRuntime Method DispatchหรือDynamic Bindingเนื่องจากการเรียกเมธอดที่ถูกแทนที่ได้รับการแก้ไขที่รันไทม์
เพื่อให้เข้าใจว่าเหตุใดจึงเป็นเช่นนั้นเรามาดูตัวอย่างMammal
และHuman
ชั้นเรียน
class Mammal {
public void speak() { System.out.println("ohlllalalalalalaoaoaoa"); }
}
class Human extends Mammal {
@Override
public void speak() { System.out.println("Hello"); }
public void speak(String language) {
if (language.equals("Hindi")) System.out.println("Namaste");
else System.out.println("Hello");
}
}
ฉันได้รวมเอาท์พุทและรหัส bytecode ไว้ในบรรทัดด้านล่างของโค้ด
Mammal anyMammal = new Mammal();
anyMammal.speak(); // Output - ohlllalalalalalaoaoaoa
// 10: invokevirtual #4 // Method org/programming/mitra/exercises/OverridingInternalExample$Mammal.speak:()V
Mammal humanMammal = new Human();
humanMammal.speak(); // Output - Hello
// 23: invokevirtual #4 // Method org/programming/mitra/exercises/OverridingInternalExample$Mammal.speak:()V
Human human = new Human();
human.speak(); // Output - Hello
// 36: invokevirtual #7 // Method org/programming/mitra/exercises/OverridingInternalExample$Human.speak:()V
human.speak("Hindi"); // Output - Namaste
// 42: invokevirtual #9 // Method org/programming/mitra/exercises/OverridingInternalExample$Human.speak:(Ljava/lang/String;)V
และจากการดูโค้ดด้านบนเราจะเห็นว่า bytecodes ของ humanMammal.speak (), human.speak () และ human.speak ("ภาษาฮินดี") นั้นแตกต่างกันโดยสิ้นเชิงเนื่องจากคอมไพเลอร์สามารถแยกความแตกต่างระหว่างสิ่งเหล่านี้ตามรายการอาร์กิวเมนต์ และการอ้างอิงชั้นเรียน และนี่คือเหตุผลวิธีมากไปเป็นที่รู้จักกันคงความแตกต่าง
แต่ bytecode สำหรับ anyMammal.speak () และ humanMammal.speak () เหมือนกันเพราะตามคอมไพเลอร์ทั้งสองวิธีถูกเรียกด้วยการอ้างอิงสัตว์เลี้ยงลูกด้วยนม แต่เอาต์พุตสำหรับการเรียกเมธอดทั้งสองแตกต่างกันเนื่องจากที่รันไทม์ JVM รู้ว่าวัตถุใดที่การอ้างอิงถืออยู่และการเรียก JVM เมธอดบนวัตถุและนี่คือสาเหตุที่ Method Overriding เรียกว่า Dynamic Polymorphism
ดังนั้นจากโค้ดด้านบนและ bytecode เป็นที่ชัดเจนว่าในระหว่างวิธีการเรียกเฟสคอมไพล์จะพิจารณาจากประเภทการอ้างอิง แต่เมธอดในเวลาดำเนินการจะถูกเรียกใช้จากอ็อบเจ็กต์ที่มีการอ้างอิงอยู่
หากคุณต้องการทราบข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้คุณสามารถอ่านเพิ่มเติมเกี่ยวกับวิธีการวิธีการที่ไม่ JVM จับมากไปและแทนที่ภายใน
Static Polymorphism: เป็นการตัดสินใจที่จะแก้ไขวิธีการที่จะทำให้สำเร็จนั้นจะถูกกำหนดในช่วงเวลาคอมไพล์ วิธีการโอเวอร์โหลดอาจเป็นตัวอย่างของสิ่งนี้
Dynamic Polymorphism: เป็นการตัดสินใจที่จะเลือกวิธีการที่จะดำเนินการโดยกำหนดไว้ในระหว่างรันไทม์ Method Overriding อาจเป็นตัวอย่างของสิ่งนี้
ความหลากหลายหมายถึงความสามารถของวัตถุในการทำงานที่แตกต่างกันสำหรับทริกเกอร์เดียวกัน
ความหลากหลายแบบคงที่ (Polymorphism เวลาคอมไพล์)
ความแตกต่างแบบไดนามิก (Runtime Polymorphism)
ความหลากหลายของเวลาคอมไพล์ (Static Binding / Early Binding):ในความหลากหลายแบบคงที่ถ้าเราเรียกเมธอดในโค้ดของเราคำจำกัดความของเมธอดนั้นจะถูกเรียกใช้จริงในเวลาคอมไพล์เท่านั้น
(หรือ)
ในขณะคอมไพล์ Java จะรู้ว่าจะเรียกใช้เมธอดใดโดยตรวจสอบลายเซ็นของเมธอด ดังนั้นสิ่งนี้เรียกว่าความหลากหลายของเวลาคอมไพล์หรือการเชื่อมแบบคงที่
Dynamic Polymorphism (Late Binding / Runtime Polymorphism):ในขณะรัน Java จะรอจนกว่ารันไทม์เพื่อตรวจสอบว่าวัตถุใดถูกชี้โดยการอ้างอิง ความละเอียดของเมธอดถูกนำมาใช้ที่รันไทม์เนื่องจากเราเรียกว่าความหลากหลายของเวลาทำงาน
พิจารณารหัสด้านล่าง:
public class X
{
public void methodA() // Base class method
{
System.out.println ("hello, I'm methodA of class X");
}
}
public class Y extends X
{
public void methodA() // Derived Class method
{
System.out.println ("hello, I'm methodA of class Y");
}
}
public class Z
{
public static void main (String args []) {
//this takes input from the user during runtime
System.out.println("Enter x or y");
Scanner scanner = new Scanner(System.in);
String value= scanner.nextLine();
X obj1 = null;
if(value.equals("x"))
obj1 = new X(); // Reference and object X
else if(value.equals("y"))
obj2 = new Y(); // X reference but Y object
else
System.out.println("Invalid param value");
obj1.methodA();
}
}
ตอนนี้การดูโค้ดคุณไม่สามารถบอกได้ว่าจะใช้ methodA () ใดเนื่องจากขึ้นอยู่กับค่าที่ผู้ใช้ให้ระหว่างรันไทม์ ดังนั้นจึงมีการตัดสินใจระหว่างรันไทม์เท่านั้นว่าจะเรียกใช้วิธีใด ดังนั้นความหลากหลายของรันไทม์
วิธีการโอเวอร์โหลดเป็นความหลากหลายของเวลาคอมไพล์ลองมาเป็นตัวอย่างเพื่อทำความเข้าใจแนวคิด
class Person //person.java file
{
public static void main ( String[] args )
{
Eat e = new Eat();
e.eat(noodle); //line 6
}
void eat (Noodles n) //Noodles is a object line 8
{
}
void eat ( Pizza p) //Pizza is a object
{
}
}
ในตัวอย่างนี้บุคคลมีวิธีการกินที่แสดงว่าเขาสามารถกินพิซซ่าหรือบะหมี่ได้ ว่าเมธอด eat นั้นโอเวอร์โหลดเมื่อเราคอมไพล์ Person.java คอมไพเลอร์แก้ไขเมธอดที่เรียกว่า "e.eat (noodles) [ซึ่งอยู่ที่บรรทัดที่ 6] ด้วยนิยามวิธีการที่ระบุไว้ในบรรทัดที่ 8 นั่นคือเมธอดที่ใช้เส้นก๋วยเตี๋ยวเป็นพารามิเตอร์ และกระบวนการทั้งหมดจะทำโดยคอมไพเลอร์ดังนั้นจึงเป็น Compile time Polymorphism กระบวนการแทนที่การเรียกเมธอดด้วยนิยามเมธอดเรียกว่าการผูกในกรณีนี้คอมไพเลอร์จะทำโดยคอมไพเลอร์จึงเรียกว่าการรวมครั้งแรก
จากคำตอบของ Naresh ความหลากหลายแบบไดนามิกเป็นเพียง 'ไดนามิก' ใน Java เนื่องจากการมีอยู่ของเครื่องเสมือนและความสามารถในการตีความรหัสในขณะทำงานแทนที่จะเป็นรหัสที่ทำงานโดยกำเนิด
ใน C ++ จะต้องได้รับการแก้ไขในเวลาคอมไพล์หากมีการคอมไพล์เป็นไบนารีดั้งเดิมโดยใช้ gcc อย่างไรก็ตาม runtime jump และ thunk ในตารางเสมือนยังคงเรียกว่า 'lookup' หรือ 'dynamic' ถ้า C สืบทอด B และคุณประกาศB* b = new C(); b->method1();
b จะได้รับการแก้ไขโดยคอมไพเลอร์ให้ชี้ไปที่วัตถุ B ภายใน C (สำหรับคลาสธรรมดาจะสืบทอดสถานการณ์คลาสวัตถุ B ภายใน C และ C จะเริ่มต้นที่ที่อยู่หน่วยความจำเดียวกันดังนั้นจึงไม่มีอะไร จะต้องทำมันจะชี้ไปที่ vptr ที่ทั้งคู่ใช้) ถ้า C สืบทอด B และ A ตารางฟังก์ชันเสมือนของวัตถุ A ภายในรายการ C สำหรับ method1 จะมีขีด จำกัด ซึ่งจะหักล้างตัวชี้ไปที่จุดเริ่มต้นของวัตถุ C ที่ห่อหุ้มแล้วส่งผ่านไปยัง A :: method1 () จริง ในส่วนข้อความที่ C ได้ลบล้าง สำหรับC* c = new C(); c->method1()
c จะชี้ไปที่วัตถุ C ด้านนอกอยู่แล้วและตัวชี้จะถูกส่งไปยัง C :: method1 () ในส่วนข้อความ อ้างถึง: http://www.programmersought.com/article/2572545946/
ใน java สำหรับB b = new C(); b.method1();
เครื่องเสมือนสามารถตรวจสอบประเภทของวัตถุที่จับคู่กับ b แบบไดนามิกและสามารถส่งผ่านตัวชี้ที่ถูกต้องและเรียกวิธีการที่ถูกต้อง ขั้นตอนพิเศษของเครื่องเสมือนช่วยลดความจำเป็นในการใช้ตารางฟังก์ชันเสมือนหรือประเภทที่ได้รับการแก้ไขในเวลาคอมไพล์แม้ว่าจะสามารถทราบได้ในเวลาคอมไพล์ก็ตาม เป็นเพียงวิธีการอื่นในการทำซึ่งเหมาะสมเมื่อมีส่วนเกี่ยวข้องกับเครื่องเสมือนและโค้ดจะถูกคอมไพล์เป็น bytecode