คุณอาจเคยเจอแนวคิดเรื่อง “การสะท้อน” ในชีวิตประจำวัน โดยปกติคำนี้หมายถึงกระบวนการศึกษาตนเอง ในการเขียนโปรแกรมมีความหมายคล้ายกัน - เป็นกลไกในการตรวจสอบข้อมูลเกี่ยวกับโปรแกรมตลอดจนการเปลี่ยนแปลงโครงสร้างและพฤติกรรมของโปรแกรมระหว่างการดำเนินการ สิ่งสำคัญที่นี่คือการดำเนินการที่รันไทม์ ไม่ใช่ในเวลาคอมไพล์ แต่ทำไมต้องตรวจสอบโค้ดตอนรันไทม์? คุณเห็นแล้ว :/
แนวคิดเรื่องการไตร่ตรองอาจไม่ชัดเจนในทันทีด้วยเหตุผลเดียว: จนถึงขณะนี้ คุณจะรู้อยู่เสมอว่าคุณกำลังเรียนชั้นเรียนอยู่ ตัวอย่างเช่น คุณสามารถเขียน class
เพื่อความกระชับ เราได้ละเว้นโค้ดสำหรับการจัดการข้อยกเว้นที่เหมาะสม เพื่อไม่ให้ใช้พื้นที่มากกว่าตัวอย่าง ในโปรแกรมจริง แน่นอนว่ามันคุ้มค่าที่จะจัดการกับสถานการณ์ที่มีการป้อนชื่อไม่ถูกต้อง ฯลฯ คอนสตรัคเตอร์เริ่มต้นนั้นค่อนข้างง่าย ดังนั้นอย่างที่คุณเห็นว่าการสร้างอินสแตนซ์ของคลาสที่ใช้งานมันนั้นไม่ใช่เรื่องยาก :) และเมื่อใช้วิธีนี้
Cat
:
package learn.javarush;
public class Cat {
private String name;
private int age;
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
public void sayMeow() {
System.out.println("Meow!");
}
public void jump() {
System.out.println("Jump!");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
คุณรู้ทุกอย่างเกี่ยวกับมัน คุณเห็นว่ามันมีสาขาและวิธีการใดบ้าง แน่นอนคุณสามารถสร้างระบบการสืบทอดด้วยคลาสทั่วไปเพื่อความสะดวกAnimal
หากจู่ๆ โปรแกรมก็ต้องการสัตว์คลาสอื่น ก่อนหน้านี้ เรายังสร้างชั้นเรียนคลินิกสัตวแพทย์ที่คุณสามารถส่งวัตถุแม่ได้Animal
และโปรแกรมจะปฏิบัติต่อสัตว์โดยขึ้นอยู่กับว่าเป็นสุนัขหรือแมว แม้ว่างานเหล่านี้จะไม่ง่ายนัก แต่โปรแกรมจะเรียนรู้ข้อมูลทั้งหมดที่ต้องการเกี่ยวกับคลาสในเวลาคอมไพล์ ดังนั้นเมื่อคุณmain()
ส่งวัตถุ ในวิธีการ Cat
ไปยังวิธีการของชั้นเรียนคลินิกสัตวแพทย์ โปรแกรมจะรู้อยู่แล้วว่านี่คือแมวไม่ใช่สุนัข ทีนี้ลองจินตนาการว่าเรากำลังเผชิญกับงานอื่น เป้าหมายของเราคือการเขียนตัววิเคราะห์โค้ด เราจำเป็นต้องสร้างคลาสCodeAnalyzer
ด้วยวิธีเดียว - void analyzeClass(Object o)
. วิธีนี้ควร:
- กำหนดคลาสที่วัตถุถูกส่งผ่านไปและแสดงชื่อคลาสในคอนโซล
- กำหนดชื่อของฟิลด์ทั้งหมดของคลาสนี้ รวมถึงฟิลด์ส่วนตัว และแสดงในคอนโซล
- กำหนดชื่อของเมธอดทั้งหมดของคลาสนี้ รวมถึงเมธอดส่วนตัว และแสดงไว้ในคอนโซล
public class CodeAnalyzer {
public static void analyzeClass(Object o) {
//Вывести название класса, к которому принадлежит an object o
//Вывести названия всех переменных этого класса
//Вывести названия всех методов этого класса
}
}
ตอนนี้ความแตกต่างระหว่างปัญหานี้กับปัญหาที่เหลือที่คุณแก้ไขก่อนหน้านี้ก็ปรากฏให้เห็นแล้ว ในกรณีนี้ปัญหาอยู่ที่ว่าคุณและโปรแกรมไม่รู้ว่าจะส่งผ่านไปยังเมธอดอะไรกันanalyzeClass()
แน่ คุณเขียนโปรแกรม โปรแกรมเมอร์คนอื่นจะเริ่มใช้งานซึ่งสามารถส่งผ่านอะไรก็ตามเข้ามาในวิธีนี้ได้ - คลาส Java มาตรฐานหรือคลาสใด ๆ ที่พวกเขาเขียน คลาสนี้สามารถมีตัวแปรและวิธีการจำนวนเท่าใดก็ได้ กล่าวอีกนัยหนึ่ง ในกรณีนี้ เรา (และโปรแกรมของเรา) ไม่รู้ว่าเราจะทำงานกับคลาสไหน แต่ถึงกระนั้นเราก็ต้องแก้ไขปัญหานี้ และที่นี่ไลบรารี Java มาตรฐานก็มาช่วยเหลือเรา - Java Reflection API Reflection API เป็นฟีเจอร์ภาษาที่ทรงพลัง เอกสารอย่างเป็นทางการของ Oracle ระบุว่ากลไกนี้แนะนำให้ใช้โดยโปรแกรมเมอร์ที่มีประสบการณ์และเข้าใจดีว่ากำลังทำอะไรอยู่เท่านั้น ในไม่ช้า คุณจะเข้าใจว่าทำไมเราถึงได้รับคำเตือนล่วงหน้าเช่นนี้ :) นี่คือรายการสิ่งที่สามารถทำได้โดยใช้ Reflection API:
- ค้นหา/กำหนดคลาสของวัตถุ
- รับข้อมูลเกี่ยวกับตัวดัดแปลงคลาส ฟิลด์ เมธอด ค่าคงที่ ตัวสร้าง และคลาสพิเศษ
- ค้นหาวิธีการที่เป็นของอินเทอร์เฟซ/อินเทอร์เฟซที่นำไปใช้
- สร้างอินสแตนซ์ของคลาสเมื่อไม่ทราบชื่อคลาสจนกว่าโปรแกรมจะถูกดำเนินการ
- รับและตั้งค่าของฟิลด์วัตถุตามชื่อ
- เรียกวิธีการของวัตถุตามชื่อ
วิธีค้นหา / กำหนดคลาสของวัตถุ
เริ่มจากพื้นฐานกันก่อน จุดเริ่มต้นของกลไกการสะท้อนของ Java คือClass
. ใช่ มันดูตลกจริงๆ แต่นั่นคือสิ่งที่สะท้อนออกมา :) เมื่อใช้ class Class
ก่อนอื่นเราจะกำหนดคลาสของอ็อบเจ็กต์ใดๆ ที่ส่งผ่านไปยังเมธอดของเรา มาลองสิ่งนี้กัน:
import learn.javarush.Cat;
public class CodeAnalyzer {
public static void analyzeClass(Object o) {
Class clazz = o.getClass();
System.out.println(clazz);
}
public static void main(String[] args) {
analyzeClass(new Cat("Barsik", 6));
}
}
เอาต์พุตคอนโซล:
class learn.javarush.Cat
ให้ความสนใจกับสองสิ่ง ประการแรก เราจงใจแยกคลาสออกCat
จากแพ็คเกจlearn.javarush;
ตอนนี้ คุณจะเห็นว่าคลาสgetClass()
ส่งคืนชื่อเต็มของคลาส ประการที่สอง เราตั้งชื่อตัวแปรของclazz
เรา ดูแปลกนิดหน่อย แน่นอนว่าควรเรียกว่า "คลาส" แต่ "คลาส" เป็นคำสงวนในภาษา Java และคอมไพเลอร์จะไม่อนุญาตให้เรียกตัวแปรในลักษณะนั้น ฉันต้องออกไปจากมัน :) ไม่ใช่การเริ่มต้นที่ไม่ดี! เรามีอะไรอีกในรายการความเป็นไปได้?
วิธีรับข้อมูลเกี่ยวกับตัวดัดแปลงคลาส ฟิลด์ เมธอด ค่าคงที่ ตัวสร้าง และซูเปอร์คลาส
มันน่าสนใจกว่านี้แล้ว! ในคลาสปัจจุบันเราไม่มีค่าคงที่และไม่มีคลาสพาเรนต์ มาเพิ่มเพื่อความสมบูรณ์กันดีกว่า มาสร้างคลาสพาเรนต์ที่ง่ายที่สุดAnimal
:
package learn.javarush;
public class Animal {
private String name;
private int age;
}
และมาเพิ่มCat
การสืบทอดจากAnimal
และหนึ่งค่าคงที่ให้กับคลาสของเรา:
package learn.javarush;
public class Cat extends Animal {
private static final String ANIMAL_FAMILY = "Семейство кошачьих";
private String name;
private int age;
//...остальная часть класса
}
ตอนนี้เรามีครบชุดแล้ว! มาลองใช้ความเป็นไปได้ของการไตร่ตรองกัน :)
import learn.javarush.Cat;
import java.util.Arrays;
public class CodeAnalyzer {
public static void analyzeClass(Object o) {
Class clazz = o.getClass();
System.out.println("Name класса: " + clazz);
System.out.println("Поля класса: " + Arrays.toString(clazz.getDeclaredFields()));
System.out.println("Родительский класс: " + clazz.getSuperclass());
System.out.println("Методы класса: " + Arrays.toString(clazz.getDeclaredMethods()));
System.out.println("Конструкторы класса: " + Arrays.toString(clazz.getConstructors()));
}
public static void main(String[] args) {
analyzeClass(new Cat("Barsik", 6));
}
}
นี่คือสิ่งที่เราได้รับในคอนโซล:
Name класса: class learn.javarush.Cat
Поля класса: [private static final java.lang.String learn.javarush.Cat.ANIMAL_FAMILY, private java.lang.String learn.javarush.Cat.name, private int learn.javarush.Cat.age]
Родительский класс: class learn.javarush.Animal
Методы класса: [public java.lang.String learn.javarush.Cat.getName(), public void learn.javarush.Cat.setName(java.lang.String), public void learn.javarush.Cat.sayMeow(), public void learn.javarush.Cat.setAge(int), public void learn.javarush.Cat.jump(), public int learn.javarush.Cat.getAge()]
Конструкторы класса: [public learn.javarush.Cat(java.lang.String,int)]
เราได้รับข้อมูลโดยละเอียดเกี่ยวกับชั้นเรียนมากมาย! และไม่ใช่แค่เรื่องสาธารณะเท่านั้น แต่ยังเกี่ยวกับเรื่องส่วนตัวด้วย ใส่ใจ: private
-ตัวแปรจะแสดงอยู่ในรายการด้วย จริงๆ แล้ว ณ จุดนี้ "การวิเคราะห์" ของชั้นเรียนถือว่าสมบูรณ์แล้ว ตอนนี้analyzeClass()
เราจะเรียนรู้ทุกสิ่งที่เป็นไปได้ โดยใช้วิธีนี้ แต่นี่ไม่ใช่ความเป็นไปได้ทั้งหมดที่เรามีเมื่อทำงานกับการไตร่ตรอง อย่าจำกัดตัวเองอยู่เพียงการสังเกตง่ายๆ และก้าวไปสู่การกระทำที่กระตือรือร้น! :)
วิธีสร้างอินสแตนซ์ของคลาสหากไม่ทราบชื่อคลาสก่อนที่โปรแกรมจะถูกดำเนินการ
เริ่มจากคอนสตรัคเตอร์เริ่มต้นกันก่อน มันยังไม่ได้อยู่ในชั้นเรียนของเราCat
ดังนั้นมาเพิ่มกัน:
public Cat() {
}
นี่คือลักษณะของโค้ดที่จะสร้างวัตถุCat
โดยใช้การสะท้อน (วิธีการcreateCat()
):
import learn.javarush.Cat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static Cat createCat() throws IOException, IllegalAccessException, InstantiationException, ClassNotFoundException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String className = reader.readLine();
Class clazz = Class.forName(className);
Cat cat = (Cat) clazz.newInstance();
return cat;
}
public static Object createObject() throws Exception {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String className = reader.readLine();
Class clazz = Class.forName(className);
Object result = clazz.newInstance();
return result;
}
public static void main(String[] args) throws IOException, IllegalAccessException, ClassNotFoundException, InstantiationException {
System.out.println(createCat());
}
}
เข้าสู่คอนโซล:
learn.javarush.Cat
เอาต์พุตคอนโซล:
Cat{name='null', age=0}
นี่ ไม่ใช่ข้อผิดพลาด: ค่าname
และage
แสดงในคอนโซลเนื่องจากเราตั้งโปรแกรมเอาต์พุตไว้ใน เมธอด toString()
คลาส Cat
ที่นี่เราอ่านชื่อของคลาสที่เราจะสร้างวัตถุจากคอนโซล โปรแกรมที่รันอยู่จะเรียนรู้ชื่อของคลาสที่วัตถุที่จะสร้าง 
newInstance()
เราจะสร้างอ็อบเจ็กต์ใหม่ของคลาสนี้ เป็นอีกเรื่องหนึ่งถ้าตัวสร้างคลาสCat
รับพารามิเตอร์เป็นอินพุต ลองลบ Constructor เริ่มต้นออกจากคลาสแล้วลองรันโค้ดของเราอีกครั้ง
null
java.lang.InstantiationException: learn.javarush.Cat
at java.lang.Class.newInstance(Class.java:427)
บางอย่างผิดพลาด! เราได้รับข้อผิดพลาดเนื่องจากเราเรียกวิธีการสร้างวัตถุผ่านตัวสร้างเริ่มต้น แต่ตอนนี้เราไม่มีนักออกแบบแบบนี้ ซึ่งหมายความว่าเมื่อวิธีการทำงานnewInstance()
กลไกการสะท้อนจะใช้ตัวสร้างเก่าของเราพร้อมกับพารามิเตอร์สองตัว:
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
แต่เราไม่ได้ทำอะไรกับพารามิเตอร์ ราวกับว่าเราลืมมันไปหมดแล้ว! หากต้องการส่งต่อให้คอนสตรัคเตอร์โดยใช้การสะท้อน คุณจะต้องปรับแต่งเล็กน้อย:
import learn.javarush.Cat;
import java.lang.reflect.InvocationTargetException;
public class Main {
public static Cat createCat() {
Class clazz = null;
Cat cat = null;
try {
clazz = Class.forName("learn.javarush.Cat");
Class[] catClassParams = {String.class, int.class};
cat = (Cat) clazz.getConstructor(catClassParams).newInstance("Barsik", 6);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return cat;
}
public static void main(String[] args) {
System.out.println(createCat());
}
}
เอาต์พุตคอนโซล:
Cat{name='Barsik', age=6}
มาดูสิ่งที่เกิดขึ้นในโปรแกรมของเรากันดีกว่า เราได้สร้างอาร์เรย์ของClass
วัตถุ
Class[] catClassParams = {String.class, int.class};
สอดคล้องกับพารามิเตอร์ของตัวสร้างของเรา (เรามีเพียงพารามิเตอร์String
และint
) เราส่งต่อไปยังวิธีการclazz.getConstructor()
และเข้าถึงตัวสร้างที่ต้องการ หลังจากนี้สิ่งที่เหลืออยู่คือการเรียกเมธอดnewInstance()
ด้วยพารามิเตอร์ที่จำเป็นและอย่าลืมส่งวัตถุไปยังคลาสที่เราต้องการอย่างชัดเจนCat
-
cat = (Cat) clazz.getConstructor(catClassParams).newInstance("Barsik", 6);
เป็นผลให้วัตถุของเราถูกสร้างขึ้นได้สำเร็จ! เอาต์พุตคอนโซล:
Cat{name='Barsik', age=6}
เดินหน้าต่อไป :)
วิธีรับและตั้งค่าของฟิลด์วัตถุตามชื่อ
ลองนึกภาพว่าคุณกำลังใช้คลาสที่เขียนโดยโปรแกรมเมอร์คนอื่น อย่างไรก็ตาม คุณไม่มีโอกาสที่จะแก้ไขมัน ตัวอย่างเช่น ไลบรารีคลาสสำเร็จรูปที่บรรจุอยู่ใน JAR คุณสามารถอ่านรหัสชั้นเรียนได้ แต่จะเปลี่ยนแปลงไม่ได้ โปรแกรมเมอร์ที่สร้างคลาสในไลบรารีนี้ (ปล่อยให้เป็นคลาสเก่าของเราCat
) นอนหลับไม่เพียงพอก่อนการออกแบบขั้นสุดท้าย และลบ getters และ setters สำหรับฟิลด์age
ออก ตอนนี้คลาสนี้มาถึงคุณแล้ว Cat
มันตรงตามความต้องการของคุณอย่างเต็มที่ เพราะคุณแค่ต้องการอ็อบเจ็กต์ในโปรแกรม แต่คุณต้องการพวกมันในสนามเดียวกันนั้นage
! นี่เป็นปัญหา: เราไม่สามารถเข้าถึงฟิลด์ได้ เนื่องจากมี modifier private
และ getters และ setters ถูกลบออกโดยผู้ที่จะเป็นผู้พัฒนาคลาสนี้ :/ การสะท้อนกลับสามารถช่วยเราในสถานการณ์นี้ได้เช่นกัน! Cat
เรามีสิทธิ์เข้าถึงรหัสชั้นเรียน : อย่างน้อยเราก็สามารถค้นหาได้ว่ามีฟิลด์ใดบ้างและเรียกว่าอะไร ด้วยข้อมูลนี้ เราจึงสามารถแก้ไขปัญหาของเราได้:
import learn.javarush.Cat;
import java.lang.reflect.Field;
public class Main {
public static Cat createCat() {
Class clazz = null;
Cat cat = null;
try {
clazz = Class.forName("learn.javarush.Cat");
cat = (Cat) clazz.newInstance();
//с полем name нам повезло - для него в классе есть setter
cat.setName("Barsik");
Field age = clazz.getDeclaredField("age");
age.setAccessible(true);
age.set(cat, 6);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
return cat;
}
public static void main(String[] args) {
System.out.println(createCat());
}
}
ตามที่ระบุไว้ในความคิดเห็นname
ทุกอย่างเป็นเรื่องง่ายในสนาม: นักพัฒนาคลาสได้จัดเตรียมผู้ตั้งค่าไว้ คุณยังรู้วิธีสร้างวัตถุจากตัวสร้างเริ่มต้น: มีวิธีสำหรับสิ่งnewInstance()
นี้ แต่คุณจะต้องคนจรจัดกับฟิลด์ที่สอง เรามาดูกันว่าเกิดอะไรขึ้นที่นี่ :)
Field age = clazz.getDeclaredField("age");
ที่นี่เราใช้วัตถุของเราClass clazz
เข้าถึงฟิลด์age
โดยใช้วิธีgetDeclaredField()
การ มันทำให้เราสามารถรับฟิลด์อายุเป็นวัตถุField age
ได้ แต่นี่ยังไม่เพียงพอ เนื่องจากprivate
ไม่สามารถกำหนดค่าฟิลด์ได้ง่ายๆ ในการดำเนินการนี้ คุณต้องทำให้ช่อง "พร้อมใช้งาน" โดยใช้วิธีsetAccessible()
:
age.setAccessible(true);
ฟิลด์เหล่านั้นที่ทำสิ่งนี้สามารถกำหนดค่าได้:
age.set(cat, 6);
อย่างที่คุณเห็นเรามีตัวตั้งค่าประเภทหนึ่งที่กลับหัว: เรากำหนดField age
ค่าให้กับฟิลด์และส่งวัตถุที่ควรกำหนดฟิลด์นี้ให้กับมันด้วย ลองใช้วิธีการของเราmain()
แล้วดู:
Cat{name='Barsik', age=6}
เยี่ยมมาก เราทำได้หมด! :) เรามาดูกันว่าเรามีความเป็นไปได้อื่นๆ อีกบ้าง...
วิธีเรียกเมธอดของวัตถุตามชื่อ
ลองเปลี่ยนสถานการณ์จากตัวอย่างก่อนหน้านี้เล็กน้อย สมมติว่าผู้พัฒนาคลาสCat
ทำผิดพลาดกับฟิลด์ - ทั้งสองฟิลด์พร้อมใช้งาน มี getters และ setters สำหรับฟิลด์เหล่านั้น ทุกอย่างก็โอเค ปัญหาแตกต่างออกไป: เขาสร้างวิธีการส่วนตัวที่เราต้องการอย่างแน่นอน:
private void sayMeow() {
System.out.println("Meow!");
}
เป็นผลให้เราจะสร้างวัตถุCat
ในโปรแกรมของเรา แต่จะไม่สามารถเรียกวิธีการของพวกเขาsayMeow()
ได้ เราจะมีแมวที่ไม่ร้องเหมียวไหม? ค่อนข้างแปลก :/ ฉันจะแก้ไขสิ่งนี้ได้อย่างไร เป็นอีกครั้งที่ Reflection API เข้ามาช่วยเหลือ! เรารู้ชื่อของวิธีการที่ต้องการ ที่เหลือเป็นเรื่องของเทคนิค:
import learn.javarush.Cat;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Main {
public static void invokeSayMeowMethod() {
Class clazz = null;
Cat cat = null;
try {
cat = new Cat("Barsik", 6);
clazz = Class.forName(Cat.class.getName());
Method sayMeow = clazz.getDeclaredMethod("sayMeow");
sayMeow.setAccessible(true);
sayMeow.invoke(cat);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
invokeSayMeowMethod();
}
}
ที่นี่เราดำเนินการในลักษณะเดียวกับในสถานการณ์ที่เข้าถึงสนามส่วนตัว ก่อนอื่น เราได้วิธีการที่เราต้องการ ซึ่งถูกห่อหุ้มไว้ใน class object Method
:
Method sayMeow = clazz.getDeclaredMethod("sayMeow");
ด้วยความช่วยเหลือgetDeclaredMethod()
คุณสามารถ "เข้าถึง" วิธีการส่วนตัวได้ ต่อไปเราจะทำให้วิธีการนั้นสามารถเรียกได้:
sayMeow.setAccessible(true);
และสุดท้าย เราเรียกเมธอดบนวัตถุที่ต้องการ:
sayMeow.invoke(cat);
การเรียกเมธอดก็ดูเหมือน "การเรียกกลับด้าน": เราคุ้นเคยกับการชี้วัตถุไปยังวิธีที่ต้องการโดยใช้จุด ( cat.sayMeow()
) และเมื่อทำงานกับการสะท้อนกลับ เราจะส่งต่อไปยังเมธอดของออบเจ็กต์ที่ต้องการเรียก . เรามีอะไรในคอนโซล?
Meow!
ทุกอย่างได้ผล! :) ตอนนี้คุณคงได้เห็นแล้วว่ากลไกการสะท้อนกลับใน Java มอบความเป็นไปได้มากมายเพียงใด ในสถานการณ์ที่ยากลำบากและไม่คาดคิด (เช่นในตัวอย่างที่มีคลาสจากไลบรารีแบบปิด) มันสามารถช่วยเราได้มากจริงๆ อย่างไรก็ตาม เช่นเดียวกับพลังอันยิ่งใหญ่อื่นๆ มันยังแสดงถึงความรับผิดชอบอันยิ่งใหญ่ด้วย ข้อเสียของการไตร่ตรองได้เขียนไว้ในส่วนพิเศษบนเว็บไซต์ของ Oracle มีข้อเสียเปรียบหลักสามประการ:
-
ผลผลิตลดลง วิธีการที่เรียกว่าใช้การสะท้อนกลับมีประสิทธิภาพต่ำกว่าวิธีที่เรียกว่าปกติ
-
มีข้อจำกัดด้านความปลอดภัย กลไกการสะท้อนช่วยให้คุณเปลี่ยนพฤติกรรมของโปรแกรมระหว่างรันไทม์ได้ แต่ในสภาพแวดล้อมการทำงานของคุณในโครงการจริงอาจมีข้อ จำกัด ที่ไม่อนุญาตให้คุณทำเช่นนี้
-
ความเสี่ยงจากการเปิดเผยข้อมูลภายใน สิ่งสำคัญคือต้องเข้าใจว่าการใช้การสะท้อนกลับเป็นการละเมิดหลักการของการห่อหุ้มโดยตรง: ทำให้เราสามารถเข้าถึงฟิลด์ส่วนตัว วิธีการ ฯลฯ ฉันคิดว่าไม่จำเป็นต้องอธิบายว่าควรใช้การละเมิดหลักการ OOP โดยตรงและร้ายแรงเฉพาะในกรณีที่ร้ายแรงที่สุดเท่านั้น เมื่อไม่มีวิธีอื่นในการแก้ปัญหาด้วยเหตุผลที่อยู่นอกเหนือการควบคุมของคุณ
GO TO FULL VERSION