ฉันใหม่กับ Java; จากการศึกษาของฉันฉันอ่านว่าการสะท้อนนั้นใช้เพื่อเรียกชั้นเรียนและวิธีการต่างๆ
ฉันควรใช้การสะท้อนเมื่อใดและอะไรคือความแตกต่างระหว่างการใช้การสะท้อนกับวัตถุที่สร้างอินสแตนซ์และวิธีการโทรในแบบดั้งเดิม
ฉันใหม่กับ Java; จากการศึกษาของฉันฉันอ่านว่าการสะท้อนนั้นใช้เพื่อเรียกชั้นเรียนและวิธีการต่างๆ
ฉันควรใช้การสะท้อนเมื่อใดและอะไรคือความแตกต่างระหว่างการใช้การสะท้อนกับวัตถุที่สร้างอินสแตนซ์และวิธีการโทรในแบบดั้งเดิม
คำตอบ:
Reflection นั้นช้ากว่าการเรียกเมธอดด้วยชื่อเพราะต้องตรวจสอบเมทาดาทาในไบต์รหัสแทนที่จะใช้ที่อยู่และค่าคงที่ที่คอมไพล์แล้ว
การสะท้อนยังมีประสิทธิภาพยิ่งขึ้น: คุณสามารถเรียกคืนคำจำกัดความของสมาชิกprotected
หรือลบการป้องกันและจัดการมันราวกับว่ามันถูกประกาศว่าไม่แน่นอน! เห็นได้ชัดว่าสิ่งนี้ทำลายการรับประกันจำนวนมากที่ภาษาปกติใช้กับโปรแกรมของคุณและอาจเป็นอันตรายอย่างยิ่งfinal
และสิ่งนี้ค่อนข้างอธิบายได้ว่าควรใช้เมื่อไร ตามปกติแล้วอย่า ถ้าคุณต้องการเรียกวิธีการเพียงแค่เรียกมัน ถ้าคุณต้องการที่จะกลายพันธุ์สมาชิกเพียงแค่ประกาศว่ามันไม่แน่นอนแทนที่จะกลับไปด้านหลังคอมไพล์
การใช้งานไตร่ตรองที่เป็นประโยชน์อย่างหนึ่งในโลกแห่งความเป็นจริงคือเมื่อทำการเขียนเฟรมเวิร์กที่ต้องทำงานร่วมกันกับคลาสที่ผู้ใช้กำหนดซึ่งผู้เขียนเฟรมเวิร์กไม่ทราบว่าสมาชิก (หรือแม้แต่คลาส) จะเป็นอย่างไร การสะท้อนกลับช่วยให้พวกเขาจัดการกับชั้นเรียนใด ๆ โดยไม่ต้องรู้ล่วงหน้า ตัวอย่างเช่นฉันไม่คิดว่าจะเป็นไปได้ที่จะเขียนไลบรารี่ที่มุ่งเน้นด้านที่ซับซ้อนโดยไม่มีการสะท้อนกลับ
เป็นอีกตัวอย่างหนึ่งที่ JUnit ใช้ในการไตร่ตรองเล็กน้อยมันจะแจกแจงวิธีการทั้งหมดในชั้นเรียนของคุณโดยถือว่าทั้งหมดนั้นเรียกว่าtestXXX
เป็นวิธีการทดสอบและดำเนินการเฉพาะที่ แต่ตอนนี้สามารถทำได้ดีกว่าด้วยคำอธิบายประกอบแทนและในความเป็นจริง JUnit 4 ได้ย้ายไปที่คำอธิบายประกอบเป็นส่วนใหญ่แทน
ฉันเป็นเหมือนคุณอีกครั้งฉันไม่รู้เรื่องการสะท้อนมากนัก - แต่ก็ไม่ได้ - แต่ฉันใช้มันครั้งเดียว
ฉันมีชั้นเรียนสองชั้นในและแต่ละชั้นมีวิธีการมากมาย
ฉันต้องการเรียกใช้วิธีการทั้งหมดในชั้นในและการเรียกใช้ด้วยตนเองจะทำงานได้มากเกินไป
ด้วยการไตร่ตรองฉันสามารถเรียกใช้วิธีการเหล่านี้ทั้งหมดในโค้ดเพียง 2-3 บรรทัดแทนที่จะเป็นจำนวนวิธี
ฉันจะใช้การไตร่ตรองเป็นสามกลุ่ม:
Reflection อนุญาตให้โปรแกรมทำงานกับโค้ดที่อาจไม่มีอยู่และทำได้ด้วยวิธีที่เชื่อถือได้
"รหัสปกติ" มีตัวอย่างข้อมูล URLConnection c = null
โดยการปรากฏตัวที่แท้จริงทำให้โหลดระดับชั้นโหลด URLConnection เป็นส่วนหนึ่งของการโหลดชั้นนี้โยนข้อยกเว้น ClassNotFound และออกจาก
Reflection อนุญาตให้คุณโหลดคลาสที่ขึ้นอยู่กับชื่อของพวกเขาในรูปแบบสตริงและทดสอบคุณสมบัติต่างๆ (มีประโยชน์สำหรับหลาย ๆ รุ่นนอกการควบคุมของคุณ) ก่อนเปิดคลาสจริงที่ขึ้นอยู่กับพวกเขา ตัวอย่างทั่วไปคือรหัสเฉพาะ OS X ที่ใช้ในการทำให้โปรแกรม Java ดูเนทีฟภายใต้ OS X ซึ่งไม่มีอยู่ในแพลตฟอร์มอื่น
พื้นฐานการไตร่ตรองหมายถึงการใช้รหัสของโปรแกรมของคุณเป็นข้อมูล
ดังนั้นการใช้ภาพสะท้อนอาจเป็นความคิดที่ดีเมื่อรหัสโปรแกรมของคุณเป็นแหล่งข้อมูลที่มีประโยชน์ (แต่มีการแลกเปลี่ยนกันดังนั้นจึงอาจไม่ใช่ความคิดที่ดีเสมอไป)
ตัวอย่างเช่นพิจารณาคลาสที่เรียบง่าย:
public class Foo {
public int value;
public string anotherValue;
}
และคุณต้องการสร้าง XML จากมัน คุณสามารถเขียนโค้ดเพื่อสร้าง XML:
public XmlNode generateXml(Foo foo) {
XmlElement root = new XmlElement("Foo");
XmlElement valueElement = new XmlElement("value");
valueElement.add(new XmlText(Integer.toString(foo.value)));
root.add(valueElement);
XmlElement anotherValueElement = new XmlElement("anotherValue");
anotherValueElement.add(new XmlText(foo.anotherValue));
root.add(anotherValueElement);
return root;
}
แต่นี่เป็นรหัสสำเร็จรูปจำนวนมากและทุกครั้งที่คุณเปลี่ยนคลาสคุณต้องอัปเดตรหัส จริงๆคุณสามารถอธิบายสิ่งที่รหัสนี้เป็น
นี่คืออัลกอริทึมและอินพุตของอัลกอริทึมคือคลาส: เราต้องการชื่อและชื่อประเภทและค่าของคุณสมบัติ นี่คือที่สะท้อนมา: มันช่วยให้คุณเข้าถึงข้อมูลนี้ Java ช่วยให้คุณตรวจสอบประเภทโดยใช้วิธีการของClass
ชั้นเรียน
บางกรณีใช้งานเพิ่มเติม:
อย่างไรก็ตามการสะท้อนกลับทั้งหมดหมายถึงไม่เพียง แต่ดูรหัสที่มีอยู่ (ซึ่งโดยตัวมันเองเรียกว่า "วิปัสสนา") แต่ยังแก้ไขหรือสร้างรหัส มีสองกรณีการใช้งานที่โดดเด่นใน Java สำหรับนี้: พร็อกซี่และ mocks
สมมติว่าคุณมีอินเทอร์เฟซ:
public interface Froobnicator {
void froobnicateFruits(List<Fruit> fruits);
void froobnicateFuel(Fuel fuel);
// lots of other things to froobnicate
}
และคุณมีการนำไปใช้งานที่ทำสิ่งที่น่าสนใจ:
public class PowerFroobnicator implements Froobnicator {
// awesome implementations
}
และในความเป็นจริงคุณมีการใช้งานที่สองเช่นกัน:
public class EnergySaverFroobnicator implements Froobnicator {
// efficient implementations
}
ตอนนี้คุณยังต้องการบันทึกผลลัพธ์บางส่วน; คุณเพียงต้องการข้อความบันทึกทุกครั้งที่มีการเรียกใช้เมธอด คุณสามารถเพิ่มบันทึกผลลัพธ์ไปยังทุกวิธีอย่างชัดเจน แต่นั่นน่ารำคาญและคุณต้องทำสองครั้ง หนึ่งครั้งต่อการติดตั้งแต่ละครั้ง (ดังนั้นยิ่งมากขึ้นเมื่อคุณเพิ่มการใช้งานเพิ่มเติม)
คุณสามารถเขียนพร็อกซีแทน:
public class LoggingFroobnicator implements Froobnicator {
private Logger logger;
private Froobnicator inner;
// constructor that sets those two
public void froobnicateFruits(List<Fruit> fruits) {
logger.logDebug("froobnicateFruits called");
inner.froobnicateFruits(fruits);
}
public void froobnicateFuel(Fuel fuel) {
logger.logDebug("froobnicateFuel( called");
inner.froobnicateFuel(fuel);
}
// lots of other things to froobnicate
}
แม้ว่าจะมีรูปแบบซ้ำ ๆ ที่สามารถอธิบายได้ด้วยอัลกอริทึม:
และอินพุตของอัลกอริทึมนี้คือนิยามอินเตอร์เฟส
Reflection อนุญาตให้คุณกำหนดคลาสใหม่โดยใช้อัลกอริทึมนี้ Java ช่วยให้คุณทำสิ่งนี้ได้โดยใช้วิธีการของjava.lang.reflect.Proxy
ชั้นเรียนและมีห้องสมุดที่ให้พลังงานมากกว่าเดิม
ดังนั้นข้อเสียของการสะท้อนคืออะไร?
การสะท้อนกลับสามารถทำให้ส่วนของโปรแกรมของคุณซิงค์โดยอัตโนมัติเมื่อก่อนหน้านี้คุณจะต้องอัปเดตโปรแกรมของคุณด้วยตนเองเพื่อใช้อินเทอร์เฟซใหม่