CodeGym /จาวาบล็อก /สุ่ม /ตัวอย่างของการสะท้อน
John Squirrels
ระดับ
San Francisco

ตัวอย่างของการสะท้อน

เผยแพร่ในกลุ่ม
คุณอาจเคยเจอแนวคิดเรื่อง "ภาพสะท้อน" ในชีวิตธรรมดาๆ คำนี้มักหมายถึงกระบวนการศึกษาตนเอง ในการเขียนโปรแกรมก็มีความหมายคล้ายๆ กัน คือเป็นกลไกในการวิเคราะห์ข้อมูลเกี่ยวกับโปรแกรม และแม้แต่การเปลี่ยนแปลงโครงสร้างและพฤติกรรมของโปรแกรมในขณะที่โปรแกรมกำลังทำงาน ตัวอย่างการสะท้อน - 1 สิ่งสำคัญที่นี่คือเรากำลังทำสิ่งนี้ในขณะรันไทม์ ไม่ใช่เวลาคอมไพล์ แต่ทำไมต้องตรวจสอบรหัสที่รันไทม์? ท้ายที่สุด คุณสามารถอ่านโค้ดได้แล้ว:/ มีเหตุผลว่าทำไมแนวคิดเรื่องการสะท้อนกลับอาจไม่ชัดเจนในทันที: จนถึงจุดนี้ คุณรู้อยู่เสมอว่าคุณกำลังทำงานกับคลาสใด ตัวอย่างเช่น คุณสามารถเขียนCatคลาส:

package learn.codegym;

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(เช่น ชั้นเรียนผู้ปกครอง) และโปรแกรมปฏิบัติต่อสัตว์อย่างเหมาะสมโดยพิจารณาจากว่าเป็นสุนัขหรือแมว แม้ว่าสิ่งเหล่านี้จะไม่ใช่งานที่ง่ายที่สุด แต่โปรแกรมก็สามารถเรียนรู้ข้อมูลที่จำเป็นทั้งหมดเกี่ยวกับคลาสได้ในเวลาคอมไพล์ ดังนั้นเมื่อคุณส่งCatวัตถุไปยังวิธีการของชั้นเรียนคลินิกสัตวแพทย์ในmain()วิธีการโปรแกรมรู้อยู่แล้วว่าเป็นแมวไม่ใช่หมา ลองจินตนาการว่าเรากำลังเผชิญกับงานที่แตกต่างออกไป เป้าหมายของเราคือการเขียนโค้ดวิเคราะห์ เราต้องสร้างCodeAnalyzerคลาสด้วยวิธีเดียว: void analyzeObject(Object o). วิธีนี้ควร:
  • กำหนดคลาสของออบเจกต์ที่ส่งไปและแสดงชื่อคลาสบนคอนโซล
  • กำหนดชื่อฟิลด์ทั้งหมดของคลาสที่ผ่าน รวมถึงไพรเวต และแสดงบนคอนโซล
  • กำหนดชื่อของเมธอดทั้งหมดของคลาสที่ผ่าน รวมถึงไพรเวต และแสดงบนคอนโซล
มันจะมีลักษณะดังนี้:

public class CodeAnalyzer {

   public static void analyzeClass(Object o) {
      
       // Print the name of the class of object o
       // Print the names of all variables of this class
       // Print the names of all methods of this class
   }
  
}
ตอนนี้เราสามารถเห็นได้อย่างชัดเจนว่างานนี้แตกต่างจากงานอื่น ๆ ที่คุณแก้ไขไปก่อนหน้านี้อย่างไร ด้วยวัตถุประสงค์ปัจจุบันของเรา ความยากอยู่ที่ข้อเท็จจริงที่ว่าทั้งเราและโปรแกรมไม่รู้ว่าอะไรจะถูกส่งต่อไปยังanalyzeClass()วิธี. หากคุณเขียนโปรแกรมดังกล่าว โปรแกรมเมอร์คนอื่นๆ จะเริ่มใช้โปรแกรมนั้น และพวกเขาอาจส่งผ่านข้อมูลใดๆ ไปยังวิธีนี้ — คลาส Java มาตรฐานใดๆ หรือคลาสอื่นๆ ที่พวกเขาเขียน คลาสที่ผ่านสามารถมีตัวแปรและเมธอดกี่ตัวก็ได้ กล่าวอีกนัยหนึ่ง เรา (และโปรแกรมของเรา) ไม่รู้ว่าเราจะทำงานกับชั้นเรียนใด แต่ถึงกระนั้นเราต้องทำงานนี้ให้สำเร็จ และนี่คือจุดที่ Java Reflection API มาตรฐานเข้ามาช่วยเหลือเรา Reflection API เป็นเครื่องมือที่มีประสิทธิภาพของภาษา เอกสารอย่างเป็นทางการของ Oracle แนะนำว่าควรใช้กลไกนี้โดยโปรแกรมเมอร์ที่มีประสบการณ์ซึ่งรู้ว่ากำลังทำอะไรอยู่เท่านั้น ในไม่ช้า คุณจะเข้าใจว่าทำไมเราจึงแจ้งเตือนล่วงหน้าแบบนี้ :) นี่คือรายการสิ่งที่คุณสามารถทำได้ด้วย Reflection API:
  1. ระบุ/กำหนดคลาสของวัตถุ
  2. รับข้อมูลเกี่ยวกับตัวดัดแปลงคลาส ฟิลด์ เมธอด ค่าคงที่ ตัวสร้าง และคลาสระดับสูง
  3. ค้นหาว่าเมธอดใดเป็นของอินเทอร์เฟซที่ใช้งานอยู่
  4. สร้างอินสแตนซ์ของคลาสที่ไม่รู้จักชื่อคลาสจนกว่าโปรแกรมจะถูกเรียกใช้งาน
  5. รับและตั้งค่าของฟิลด์อินสแตนซ์ตามชื่อ
  6. เรียกวิธีการอินสแตนซ์ด้วยชื่อ
รายการที่น่าประทับใจใช่มั้ย :) บันทึก:กลไกการสะท้อนสามารถทำสิ่งเหล่านี้ได้ "ทันที" โดยไม่คำนึงถึงประเภทของวัตถุที่เราส่งไปยังเครื่องวิเคราะห์รหัสของเรา! ลองสำรวจความสามารถของ Reflection API โดยดูตัวอย่างบางส่วน

วิธีระบุ/กำหนดคลาสของวัตถุ

เริ่มจากพื้นฐานกันก่อน จุดเริ่มต้นของเอ็นจิ้นการสะท้อนของ Java คือClassคลาส ใช่ มันดูตลกมาก แต่นั่นคือการสะท้อนกลับ :) เมื่อใช้Classคลาส เราจะกำหนดคลาสของออบเจกต์ใดๆ ที่ส่งไปยังเมธอดของเราก่อน ลองทำสิ่งนี้:

import learn.codegym.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("Fluffy", 6));
   }
}
เอาต์พุตคอนโซล:

class learn.codegym.Cat
ให้ความสนใจกับสองสิ่ง ขั้นแรก เราจงใจใส่Catคลาสในlearn.codegymแพ็คเกจ แยกต่างหาก ตอนนี้คุณจะเห็นว่าgetClass()เมธอดส่งคืนชื่อเต็มของคลาส ประการที่สอง เราตั้งชื่อตัวแปรของclazzเรา มันดูแปลกไปหน่อย มันสมเหตุสมผลที่จะเรียกมันว่า "คลาส" แต่ "คลาส" เป็นคำสงวนใน Java คอมไพเลอร์ไม่อนุญาตให้เรียกตัวแปรนั้น เราต้องหลีกเลี่ยงสิ่งนั้น :) ไม่เลวสำหรับการเริ่มต้น! เรามีอะไรอีกบ้างในรายการความสามารถนั้น

วิธีรับข้อมูลเกี่ยวกับตัวดัดแปลงคลาส ฟิลด์ เมธอด ค่าคงที่ ตัวสร้าง และคลาสระดับสูง

ตอนนี้สิ่งที่น่าสนใจมากขึ้น! ในคลาสปัจจุบัน เราไม่มีค่าคงที่หรือคลาสพาเรนต์ มาเพิ่มเพื่อสร้างภาพที่สมบูรณ์ สร้างAnimalคลาสผู้ปกครองที่ง่ายที่สุด:

package learn.codegym;
public class Animal {

   private String name;
   private int age;
}
และเราจะทำการCatสืบทอดคลาส ของเรา Animalและเพิ่มค่าคงที่หนึ่งค่า:

package learn.codegym;

public class Cat extends Animal {

   private static final String ANIMAL_FAMILY = "Feline family";

   private String name;
   private int age;

   // ...the rest of the class
}
ตอนนี้เรามีภาพที่สมบูรณ์แล้ว! มาดูกันว่าแสงสะท้อนสามารถทำอะไรได้บ้าง :)

import learn.codegym.Cat;

import java.util.Arrays;

public class CodeAnalyzer {

   public static void analyzeClass(Object o) {
       Class clazz = o.getClass();
       System.out.println("Class name: " + clazz);
       System.out.println("Class fields: " + Arrays.toString(clazz.getDeclaredFields()));
       System.out.println("Parent class: " + clazz.getSuperclass());
       System.out.println("Class methods: " + Arrays.toString(clazz.getDeclaredMethods()));
       System.out.println("Class constructors: " + Arrays.toString(clazz.getConstructors()));
   }

   public static void main(String[] args) {

       analyzeClass(new Cat("Fluffy", 6));
   }
}
นี่คือสิ่งที่เราเห็นบนคอนโซล:

Class name:  class learn.codegym.Cat 
Class fields: [private static final java.lang.String learn.codegym.Cat.ANIMAL_FAMILY, private java.lang.String learn.codegym.Cat.name, private int learn.codegym.Cat.age] 
Parent class: class learn.codegym.Animal 
Class methods: [public java.lang.String learn.codegym.Cat.getName(), public void learn.codegym.Cat.setName(java.lang.String), public void learn.codegym.Cat.sayMeow(), public void learn.codegym.Cat.setAge(int), public void learn.codegym.Cat.jump(), public int learn.codegym.Cat.getAge()] 
Class constructors: [public learn.codegym.Cat(java.lang.String, int)]
ดูข้อมูลคลาสโดยละเอียดทั้งหมดที่เราสามารถรับได้! และไม่ใช่แค่ข้อมูลสาธารณะ แต่ยังรวมถึงข้อมูลส่วนตัวด้วย! บันทึก: privateตัวแปรจะแสดงในรายการด้วย "การวิเคราะห์" ชั้นเรียนของเราถือได้ว่าสมบูรณ์โดยพื้นฐานแล้ว: เรากำลังใช้analyzeObject()วิธีการเพื่อเรียนรู้ทุกสิ่งที่เราทำได้ แต่นี่ไม่ใช่ทุกสิ่งที่เราสามารถทำได้ด้วยการไตร่ตรอง เราไม่ได้จำกัดอยู่แค่การสังเกตง่ายๆ — เราจะดำเนินการต่อไป! :)

วิธีสร้างอินสแตนซ์ของคลาสที่ไม่รู้จักชื่อคลาสจนกว่าโปรแกรมจะถูกเรียกใช้งาน

เริ่มจากตัวสร้างเริ่มต้นกันก่อน ชั้นเรียน ของเราCatยังไม่มี ดังนั้นขอเพิ่ม:

public Cat() {
  
}
นี่คือรหัสสำหรับสร้างCatวัตถุโดยใช้การสะท้อน ( createCat()วิธีการ ):

import learn.codegym.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.codegym.Cat
เอาต์พุตคอนโซล:

Cat{name='null', age=0}
นี่ไม่ใช่ข้อผิดพลาด: ค่าของnameและageแสดงบนคอนโซลเนื่องจากเราเขียนโค้ดเพื่อส่งออกค่าเหล่านี้ในtoString()เมธอดของCatคลาส ที่นี่เราอ่านชื่อของคลาสที่เราจะสร้างวัตถุจากคอนโซล โปรแกรมรู้จักชื่อของคลาสที่จะสร้างวัตถุ ตัวอย่างการสะท้อน - 3เพื่อความกะทัดรัด เราได้ละเว้นรหัสการจัดการข้อยกเว้นที่เหมาะสม ซึ่งจะใช้พื้นที่มากกว่าตัวอย่าง ในโปรแกรมจริง แน่นอน คุณควรจัดการกับสถานการณ์ที่เกี่ยวข้องกับชื่อที่ป้อนไม่ถูกต้อง ฯลฯ ตัวสร้างเริ่มต้นนั้นค่อนข้างเรียบง่าย ดังนั้นอย่างที่คุณเห็น มันง่ายที่จะใช้มันเพื่อสร้างอินสแตนซ์ของคลาส :) ใช้วิธีnewInstance()การ เราสร้างวัตถุใหม่ของคลาสนี้ เป็นอีกเรื่องหนึ่งหากCatตัวสร้างใช้อาร์กิวเมนต์เป็นอินพุต ลองลบตัวสร้างเริ่มต้นของคลาสออกแล้วลองรันโค้ดของเราอีกครั้ง

null
java.lang.InstantiationException: learn.codegym.Cat 
at java.lang.Class.newInstance(Class.java:427)
บางอย่างผิดพลาด! เราพบข้อผิดพลาดเนื่องจากเราเรียกเมธอดเพื่อสร้างวัตถุโดยใช้ตัวสร้างเริ่มต้น แต่ตอนนี้เราไม่มีคอนสตรัคเตอร์ ดังนั้นเมื่อnewInstance()เมธอดทำงาน กลไกการสะท้อนจะใช้ตัวสร้างเก่าของเราที่มีพารามิเตอร์สองตัว:

public Cat(String name, int age) {
   this.name = name;
   this.age = age;
}
แต่เราไม่ได้ทำอะไรกับพารามิเตอร์ ราวกับว่าเราลืมไปหมดแล้ว! การใช้การสะท้อนกลับเพื่อส่งอาร์กิวเมนต์ไปยังตัวสร้างต้องใช้ "ความคิดสร้างสรรค์" เล็กน้อย:

import learn.codegym.Cat;

import java.lang.reflect.InvocationTargetException;

public class Main {

   public static Cat createCat()  {

       Class clazz = null;
       Cat cat = null;

       try {
           clazz = Class.forName("learn.codegym.Cat");
           Class[] catClassParams = {String.class, int.class};
           cat = (Cat) clazz.getConstructor(catClassParams).newInstance("Fluffy", 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='Fluffy', age=6}
มาดูกันดีกว่าว่ามีอะไรเกิดขึ้นในโปรแกรมของเราบ้าง เราสร้างอาร์เรย์ของClassวัตถุ

Class[] catClassParams = {String.class, int.class};
สอดคล้องกับพารามิเตอร์ของตัวสร้างของเรา (ซึ่งเพิ่งมีStringและintพารามิเตอร์) เราส่งผ่านไปยังclazz.getConstructor()วิธีการและเข้าถึงตัวสร้างที่ต้องการ หลังจากนั้น สิ่งที่เราต้องทำคือเรียกใช้newInstance()เมธอดพร้อมอาร์กิวเมนต์ที่จำเป็น และอย่าลืมแปลงออบเจกต์เป็นประเภทที่ต้องการอย่างชัดเจนCat:

cat = (Cat) clazz.getConstructor(catClassParams).newInstance("Fluffy", 6);
ตอนนี้สร้างวัตถุของเราสำเร็จแล้ว! เอาต์พุตคอนโซล:

Cat{name='Fluffy', age=6}
ก้าวไปพร้อมกัน :)

วิธีรับและตั้งค่าของฟิลด์อินสแตนซ์ตามชื่อ

ลองจินตนาการว่าคุณกำลังใช้คลาสที่เขียนโดยโปรแกรมเมอร์คนอื่น นอกจากนี้ คุณไม่มีความสามารถในการแก้ไข ตัวอย่างเช่น ไลบรารีคลาสสำเร็จรูปที่บรรจุใน JAR คุณสามารถอ่านรหัสของชั้นเรียนได้ แต่คุณไม่สามารถเปลี่ยนแปลงได้ สมมติว่าโปรแกรมเมอร์ที่สร้างคลาสหนึ่งในไลบรารีนี้ (ปล่อยให้เป็นCatคลาสเก่าของเรา) นอนหลับไม่เพียงพอในคืนก่อนที่การออกแบบจะเสร็จสิ้น ได้ลบตัวรับและตัวตั้งค่าสำหรับฟิลด์ageออก ตอนนี้คลาสนี้มาหาคุณแล้ว มันตอบสนองทุกความต้องการของคุณ เนื่องจากคุณเพียงแค่ต้องการCatวัตถุในโปรแกรมของคุณ แต่คุณต้องการให้มีageสนาม! นี่คือปัญหา: เราไม่สามารถไปถึงสนามได้ เพราะมันมีprivateตัวดัดแปลงและตัวรับและตัวตั้งถูกลบโดยนักพัฒนาที่อดหลับอดนอนซึ่งสร้างคลาส :/ การไตร่ตรองสามารถช่วยเราในสถานการณ์นี้ได้! เรามีสิทธิ์เข้าถึงรหัสสำหรับCatชั้นเรียน อย่างน้อยเราก็สามารถค้นหาได้ว่ามีฟิลด์ใดบ้างและเรียกว่าอะไร ด้วยข้อมูลนี้ เราสามารถแก้ปัญหาของเรา:

import learn.codegym.Cat;

import java.lang.reflect.Field;

public class Main {

   public static Cat createCat()  {

       Class clazz = null;
       Cat cat = null;
       try {
           clazz = Class.forName("learn.codegym.Cat");
           cat = (Cat) clazz.newInstance();

           // We got lucky with the name field, since it has a setter
           cat.setName("Fluffy");

           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ฟิลด์นั้นตรงไปตรงมา เนื่องจากผู้พัฒนาคลาสได้จัดเตรียม setter ไว้ให้ คุณรู้วิธีสร้างวัตถุจากตัวสร้างเริ่มต้นแล้ว: เรามีสิ่งnewInstance()นี้ แต่เราจะต้องทำการซ่อมกับฟิลด์ที่สอง มาดูกันว่าเกิดอะไรขึ้นที่นี่ :)

Field age = clazz.getDeclaredField("age");
ที่นี่ ใช้Class clazzวัตถุของเรา เราเข้าถึงageฟิลด์ผ่านgetDeclaredField()เมธอด มันช่วยให้เราได้รับฟิลด์อายุเป็นField ageวัตถุ แต่ยังไม่เพียงพอ เพราะเราไม่สามารถกำหนดค่าให้กับprivateฟิลด์ได้ ในการทำเช่นนี้ เราจำเป็นต้องทำให้ฟิลด์สามารถเข้าถึงได้โดยใช้setAccessible()วิธีการ:

age.setAccessible(true);
เมื่อเราทำสิ่งนี้กับฟิลด์แล้ว เราสามารถกำหนดค่าได้:

age.set(cat, 6);
อย่างที่คุณเห็นField ageออบเจกต์ของเรามีตัวเซ็ตแบบ inner-out ที่เราส่งผ่านค่า int และอ็อบเจกต์ที่จะกำหนดฟิลด์ให้ เราเรียกใช้main()วิธีการของเราและดู:

Cat{name='Fluffy', age=6}
ยอดเยี่ยม! เราทำได้! :) มาดูกันว่าเราจะทำอะไรได้อีก...

วิธีเรียกวิธีการอินสแตนซ์ด้วยชื่อ

ลองเปลี่ยนสถานการณ์เล็กน้อยในตัวอย่างก่อนหน้านี้ สมมติว่าCatผู้พัฒนาคลาสไม่ได้ทำผิดพลาดกับ getters และ setters ทุกอย่างโอเคในเรื่องนั้น ตอนนี้ปัญหาแตกต่างออกไป: มีวิธีการที่เราต้องการอย่างแน่นอน แต่ผู้พัฒนากำหนดให้เป็นส่วนตัว:

private void sayMeow() {

   System.out.println("Meow!");
}
ซึ่งหมายความว่าถ้าเราสร้างCatวัตถุในโปรแกรมของเรา เราจะไม่สามารถเรียกใช้sayMeow()เมธอดกับวัตถุนั้นได้ เราจะมีแมวที่ไม่เหมียว? แปลกมาก :/ เราจะแก้ไขปัญหานี้อย่างไร? Reflection API ช่วยเราอีกครั้ง! เรารู้ชื่อวิธีการที่เราต้องการ อย่างอื่นเป็นเทคนิค:

import learn.codegym.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("Fluffy", 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();
   }
}
ที่นี่เราทำสิ่งเดียวกันกับที่เราทำเมื่อเข้าถึงฟิลด์ส่วนตัว ขั้นแรก เราได้วิธีการที่เราต้องการ มันถูกห่อหุ้มไว้ในMethodวัตถุ:

Method sayMeow = clazz.getDeclaredMethod("sayMeow");
วิธีการ นี้getDeclaredMethod()ช่วยให้เราได้รับวิธีการส่วนตัว ต่อไป เราทำให้เมธอดนี้เรียกใช้ได้:

sayMeow.setAccessible(true);
และสุดท้าย เราเรียกเมธอดบนวัตถุที่ต้องการ:

sayMeow.invoke(cat);
ที่นี่ การเรียกใช้เมธอดของเราดูเหมือน "การเรียกกลับ": เราคุ้นเคยกับการใช้จุดเพื่อชี้วัตถุไปยังเมธอดที่ต้องการ ( ) cat.sayMeow()แต่เมื่อทำงานกับการสะท้อนกลับ เราจะส่งผ่านไปยังเมธอดของวัตถุที่เราต้องการเรียก วิธีการนั้น มีอะไรอยู่บนคอนโซลของเราบ้าง?

Meow!
ทุกอย่างได้ผล! :) ตอนนี้คุณสามารถเห็นความเป็นไปได้มากมายที่กลไกการสะท้อนของ Java มอบให้เรา ในสถานการณ์ที่ยากลำบากและไม่คาดคิด (เช่น ตัวอย่างของเราที่มีชั้นเรียนจากห้องสมุดปิด) สิ่งนี้สามารถช่วยเราได้มาก แต่เช่นเดียวกับอำนาจอันยิ่งใหญ่ใดๆ อำนาจนั้นก็นำมาซึ่งความรับผิดชอบที่ยิ่งใหญ่เช่นกัน ข้อเสียของการสะท้อนได้อธิบายไว้ในส่วนพิเศษบนเว็บไซต์ Oracle มีสามข้อเสียหลัก:
  1. ประสิทธิภาพแย่ลง วิธีที่เรียกว่าการใช้การสะท้อนกลับมีประสิทธิภาพที่แย่กว่าวิธีที่เรียกว่าวิธีปกติ

  2. มีข้อจำกัดด้านความปลอดภัย กลไกการสะท้อนช่วยให้เราเปลี่ยนพฤติกรรมของโปรแกรมในขณะรันไทม์ แต่ในสถานที่ทำงานของคุณ เมื่อทำงานในโครงการจริง คุณอาจเผชิญกับข้อจำกัดที่ไม่อนุญาต

  3. ความเสี่ยงจากการเปิดเผยข้อมูลภายใน สิ่งสำคัญคือต้องเข้าใจว่าการสะท้อนเป็นการละเมิดหลักการของการห่อหุ้มโดยตรง: มันช่วยให้เราเข้าถึงฟิลด์ส่วนตัว วิธีการ ฯลฯ ฉันไม่คิดว่าจำเป็นต้องพูดถึงว่าควรใช้การละเมิดหลักการของ OOP โดยตรงและโจ่งแจ้ง เฉพาะในกรณีที่รุนแรงที่สุด เมื่อไม่มีวิธีอื่นในการแก้ปัญหาด้วยเหตุผลที่อยู่นอกเหนือการควบคุมของคุณ

ใช้การไตร่ตรองอย่างชาญฉลาดและเฉพาะในสถานการณ์ที่ไม่สามารถหลีกเลี่ยงได้ และอย่าลืมเกี่ยวกับข้อบกพร่องของมัน ด้วยเหตุนี้บทเรียนของเราจึงสิ้นสุดลง มันค่อนข้างยาว แต่คุณได้เรียนรู้มากมายในวันนี้ :)
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION