Câu hỏi Sự phản chiếu là gì và tại sao nó lại hữu ích?


Phản chiếu là gì và tại sao lại hữu ích?

Tôi đặc biệt quan tâm đến Java, nhưng tôi cho rằng các nguyên tắc đều giống nhau ở bất kỳ ngôn ngữ nào.


1696
2017-09-01 08:39


gốc


Đối với tôi, đó là một cách để có được tên lớp trong thời gian chạy và tạo ra các đối tượng của lớp đó. - Nabin
bởi vì đây là một câu hỏi phổ biến tôi muốn chỉ ra rằng sự phản chiếu (không có chú thích) nên là công cụ cuối cùng bạn đi đến khi giải quyết một vấn đề. Tôi sử dụng nó và yêu thích nó, nhưng nó hủy bỏ tất cả những ưu điểm của việc gõ tĩnh của Java. Nếu bạn cần nó, cô lập nó thành một khu vực nhỏ nhất có thể (Một phương pháp hoặc một lớp). Nó được chấp nhận hơn để sử dụng nó trong các bài kiểm tra hơn là mã sản xuất. Với chú thích, nó sẽ là tốt - Điểm chính là không chỉ định tên lớp hoặc phương thức là "Chuỗi" nếu bạn có thể tránh được nó. - Bill K
Xem thêm: softwareengineering.stackexchange.com/questions/123956/… - givanse


Các câu trả lời:


Phản chiếu tên được sử dụng để mô tả mã có thể kiểm tra mã khác trong cùng một hệ thống (hoặc chính nó).

Ví dụ: giả sử bạn có một đối tượng thuộc loại không xác định trong Java và bạn muốn gọi phương thức 'doSomething' trên đó nếu có. Hệ thống gõ tĩnh của Java không thực sự được thiết kế để hỗ trợ điều này trừ khi đối tượng phù hợp với giao diện đã biết, nhưng sử dụng sự phản chiếu, mã của bạn có thể nhìn vào đối tượng và tìm ra nó có phương thức gọi là 'doSomething' hay không. muốn.

Vì vậy, để cung cấp cho bạn một ví dụ mã về điều này trong Java (hãy tưởng tượng đối tượng được đề cập là foo):

Method method = foo.getClass().getMethod("doSomething", null);
method.invoke(foo, null);

Một trường hợp sử dụng rất phổ biến trong Java là việc sử dụng các chú thích. Ví dụ, JUnit 4 sẽ sử dụng sự phản chiếu để xem qua các lớp của bạn cho các phương thức được gắn thẻ với chú thích @Test, và sau đó sẽ gọi chúng khi chạy kiểm tra đơn vị.

Có một số ví dụ phản chiếu tốt để bạn bắt đầu http://docs.oracle.com/javase/tutorial/reflect/index.html

Và cuối cùng, có, các khái niệm khá giống với các ngôn ngữ kiểu tĩnh khác có hỗ trợ sự phản chiếu (như C #). Trong các ngôn ngữ được gõ động, trường hợp sử dụng được mô tả ở trên ít cần thiết hơn (vì trình biên dịch sẽ cho phép bất kỳ phương thức nào được gọi trên bất kỳ đối tượng nào, không chạy khi nó không tồn tại), nhưng trường hợp thứ hai tìm kiếm các phương thức được đánh dấu hoặc làm việc theo một cách nào đó vẫn còn phổ biến.

Cập nhật từ nhận xét:

Khả năng kiểm tra mã trong hệ thống và xem các loại đối tượng là   không phản ánh, nhưng thay vào đó là Introspection Type. Sự phản chiếu là sau đó   khả năng thực hiện các sửa đổi trong thời gian chạy bằng cách sử dụng   nội tâm. Sự khác biệt là cần thiết ở đây như một số ngôn ngữ   hỗ trợ nội tâm, nhưng không hỗ trợ sự phản chiếu. Một ví dụ như vậy   là C ++


1417
2017-09-01 08:44



u có thể giải thích ý nghĩa của tham số null trong dòng này Method method = foo.getClass (). getMethod ("doSomething", null); - Krsna Chaitanya
Null cho biết không có tham số nào được truyền cho phương thức foo. Xem docs.oracle.com/javase/6/docs/api/java/lang/reflect/…, java.lang.Object ...) để biết chi tiết. - Matt Sheppard
Chỉ cần để làm sáng tỏ vì điều này có rất nhiều upvotes. Khả năng kiểm tra mã trong hệ thống và xem các kiểu đối tượng không phải là sự phản chiếu, mà là kiểu Introspection. Sự phản xạ sau đó là khả năng thực hiện các sửa đổi trong thời gian chạy bằng cách sử dụng nội tâm. Sự khác biệt là cần thiết ở đây vì một số ngôn ngữ hỗ trợ nội tâm, nhưng không hỗ trợ sự phản chiếu. Một ví dụ như vậy là C ++. - bigtunacan
Tôi thích sự phản chiếu nhưng nếu bạn có quyền kiểm soát mã, thì việc sử dụng sự phản chiếu như được chỉ rõ trong câu trả lời này là đơn nhất và do đó một sự lạm dụng - Bạn nên sử dụng kiểu Introspection (instanceof) và các kiểu mạnh. Nếu có bất kỳ cách nào nhưng phản ánh để làm một cái gì đó, đó là cách nó nên được thực hiện. Phản ánh gây đau lòng nghiêm trọng vì bạn mất tất cả các lợi thế của việc sử dụng ngôn ngữ được nhập tĩnh. Nếu bạn cần nó, bạn cần nó, tuy nhiên ngay cả sau đó tôi sẽ xem xét một giải pháp đóng gói sẵn như Spring hoặc một cái gì đó hoàn toàn đóng gói sự phản ánh cần thiết - IE: để cho người khác bị đau đầu. - Bill K
@bigtunacan Bạn lấy thông tin đó từ đâu? Tôi thấy thuật ngữ "phản chiếu" được sử dụng trong tài liệu Java chính thức từ Oracle để mô tả không chỉ khả năng thay đổi thời gian chạy mà còn khả năng xem loại đối tượng. Chưa kể rằng hầu hết cái gọi là "loại nội quan" liên quan đến các lớp học (ví dụ: Method, Constructor, Modifier, Field, Member, về cơ bản tất cả ngoại trừ Class) nằm trong java.lang.*reflect* gói. Có lẽ khái niệm "phản chiếu" toàn diện bao gồm cả "loại nội tâm" và sửa đổi tại thời gian chạy? - RestInPeace


Sự phản chiếu là khả năng của một ngôn ngữ để kiểm tra và tự động gọi các lớp học, phương pháp, các thuộc tính, vv trong thời gian chạy.

Ví dụ, tất cả các đối tượng trong Java có phương thức getClass(), cho phép bạn xác định lớp của đối tượng ngay cả khi bạn không biết nó ở thời gian biên dịch (ví dụ: nếu bạn khai báo nó là một Object) - điều này có vẻ tầm thường, nhưng phản ánh như vậy là không thể trong các ngôn ngữ ít năng động hơn như C++. Các ứng dụng nâng cao hơn cho phép bạn liệt kê và gọi các phương thức, các nhà xây dựng, v.v.

Sự phản chiếu là quan trọng vì nó cho phép bạn viết các chương trình mà không phải "biết" mọi thứ vào thời gian biên dịch, làm cho chúng năng động hơn, vì chúng có thể được gắn với nhau khi chạy. Mã này có thể được viết dựa trên các giao diện đã biết, nhưng các lớp thực tế được sử dụng có thể được khởi tạo bằng cách sử dụng sự phản chiếu từ các tệp cấu hình.

Rất nhiều khung công tác hiện đại sử dụng sự phản chiếu rộng rãi cho chính lý do này. Hầu hết các ngôn ngữ hiện đại khác cũng sử dụng sự phản chiếu, và trong các ngôn ngữ kịch bản (như Python) chúng được tích hợp chặt chẽ hơn, vì nó cảm thấy tự nhiên hơn trong mô hình lập trình chung của các ngôn ngữ đó.


195
2017-09-01 08:52





Một trong những cách sử dụng phản chiếu yêu thích của tôi là phương thức kết xuất Java bên dưới. Phải mất bất kỳ đối tượng nào làm tham số và sử dụng API phản chiếu Java in ra mọi tên và giá trị của trường.

import java.lang.reflect.Array;
import java.lang.reflect.Field;

public static String dump(Object o, int callCount) {
    callCount++;
    StringBuffer tabs = new StringBuffer();
    for (int k = 0; k < callCount; k++) {
        tabs.append("\t");
    }
    StringBuffer buffer = new StringBuffer();
    Class oClass = o.getClass();
    if (oClass.isArray()) {
        buffer.append("\n");
        buffer.append(tabs.toString());
        buffer.append("[");
        for (int i = 0; i < Array.getLength(o); i++) {
            if (i < 0)
                buffer.append(",");
            Object value = Array.get(o, i);
            if (value.getClass().isPrimitive() ||
                    value.getClass() == java.lang.Long.class ||
                    value.getClass() == java.lang.String.class ||
                    value.getClass() == java.lang.Integer.class ||
                    value.getClass() == java.lang.Boolean.class
                    ) {
                buffer.append(value);
            } else {
                buffer.append(dump(value, callCount));
            }
        }
        buffer.append(tabs.toString());
        buffer.append("]\n");
    } else {
        buffer.append("\n");
        buffer.append(tabs.toString());
        buffer.append("{\n");
        while (oClass != null) {
            Field[] fields = oClass.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                buffer.append(tabs.toString());
                fields[i].setAccessible(true);
                buffer.append(fields[i].getName());
                buffer.append("=");
                try {
                    Object value = fields[i].get(o);
                    if (value != null) {
                        if (value.getClass().isPrimitive() ||
                                value.getClass() == java.lang.Long.class ||
                                value.getClass() == java.lang.String.class ||
                                value.getClass() == java.lang.Integer.class ||
                                value.getClass() == java.lang.Boolean.class
                                ) {
                            buffer.append(value);
                        } else {
                            buffer.append(dump(value, callCount));
                        }
                    }
                } catch (IllegalAccessException e) {
                    buffer.append(e.getMessage());
                }
                buffer.append("\n");
            }
            oClass = oClass.getSuperclass();
        }
        buffer.append(tabs.toString());
        buffer.append("}\n");
    }
    return buffer.toString();
}

84
2017-09-02 16:15



Số lượng cuộc gọi nên được đặt thành? - Tom
Tôi có ngoại lệ trong chuỗi "AWT-EventQueue-0" java.lang.StackOverflowError khi tôi chạy nó. - Tom
@Tom callCount nên được đặt thành 0. Giá trị của nó được sử dụng để xác định có bao nhiêu tab nên đứng trước mỗi dòng đầu ra: mỗi lần kết xuất cần phải đổ một "subobject", đầu ra sẽ in như lồng nhau trong phần tử cha. Phương pháp đó chứng tỏ hữu ích khi được bọc trong một phương pháp khác. Xem xét printDump(Object obj){ System.out.println(dump(obj, 0)); }. - fny
Java.lang.StackOverflowError có thể được tạo ra trong trường hợp tham chiếu vòng tròn, vì đệ quy không được kiểm tra: buffer.append (dump (value, callCount)) - Arnaud P
Bạn có thể phát hành mã của mình một cách cụ thể cho miền công cộng không? - stolsvik


Sử dụng phản ánh

Phản ánh thường được sử dụng bởi các chương trình đòi hỏi khả năng kiểm tra hoặc sửa đổi hành vi thời gian chạy của các ứng dụng đang chạy trong máy ảo Java. Đây là một tính năng tương đối nâng cao và chỉ nên được sử dụng bởi các nhà phát triển có hiểu biết sâu sắc về các nguyên tắc cơ bản của ngôn ngữ. Với suy nghĩ trước đó, sự phản chiếu là một kỹ thuật mạnh mẽ và có thể cho phép các ứng dụng thực hiện các hoạt động mà nếu không sẽ là không thể.

Tính năng mở rộng

Một ứng dụng có thể sử dụng các lớp bên ngoài, do người dùng định nghĩa bằng cách tạo ra các cá thể các đối tượng mở rộng bằng cách sử dụng các tên đầy đủ của họ. Trình duyệt lớp và môi trường phát triển trực quan Một trình duyệt lớp cần có khả năng liệt kê các thành viên của các lớp. Môi trường phát triển trực quan có thể được hưởng lợi từ việc sử dụng thông tin kiểu có sẵn trong sự phản ánh để hỗ trợ nhà phát triển viết mã đúng. Công cụ kiểm tra và gỡ lỗi Các trình gỡ rối cần có khả năng kiểm tra các thành viên riêng trong lớp học. Các bộ khai thác thử nghiệm có thể sử dụng sự phản chiếu để có hệ thống gọi các API bộ có thể phát hiện được xác định trên một lớp, để đảm bảo mức độ bao phủ mã cao trong một bộ kiểm thử.

Những hạn chế của sự phản chiếu

Phản ánh là mạnh mẽ, nhưng không nên được sử dụng bừa bãi. Nếu có thể thực hiện một thao tác mà không sử dụng phản xạ, thì tốt hơn là tránh sử dụng nó. Những lưu ý sau cần được lưu ý khi truy cập mã thông qua sự phản chiếu.

  • Hiệu suất chi phí

Bởi vì sự phản chiếu liên quan đến các kiểu được giải quyết động, một số tối ưu hóa máy ảo Java không thể được thực hiện. Do đó, các hoạt động phản xạ có hiệu suất chậm hơn so với các đối tác không phản chiếu của chúng và nên tránh trong các phần mã được gọi thường xuyên trong các ứng dụng nhạy cảm với hiệu năng.

  • Giới hạn bảo mật

Phản ánh yêu cầu một sự cho phép thời gian chạy mà có thể không có mặt khi chạy dưới một trình quản lý bảo mật. Đây là một vấn đề quan trọng đối với mã phải chạy trong một ngữ cảnh bảo mật bị hạn chế, chẳng hạn như trong một Applet.

  • Phơi nhiễm nội bộ

Vì sự phản chiếu cho phép mã thực hiện các hoạt động bất hợp pháp trong mã không phản chiếu, như truy cập các trường và phương thức riêng, việc sử dụng sự phản chiếu có thể dẫn đến các tác dụng phụ không mong muốn, có thể khiến mã rối loạn chức năng và có thể phá hủy tính di động. Phản chiếu phá vỡ mã trừu tượng và do đó có thể thay đổi hành vi với nâng cấp của nền tảng này.

nguồn: API phản chiếu


56
2017-10-17 11:59





Phản ánh là một cơ chế quan trọng để cho phép ứng dụng hoặc khung làm việc với mã có thể thậm chí chưa được viết!

Lấy ví dụ cho tệp web.xml điển hình của bạn. Điều này sẽ chứa một danh sách các phần tử servlet, chứa các phần tử lớp servlet lồng nhau. Thùng chứa servlet sẽ xử lý tệp web.xml và tạo mới một cá thể mới của mỗi lớp servlet thông qua sự phản chiếu.

Một ví dụ khác là Java API cho phân tích cú pháp XML (JAXP). Trong trường hợp nhà cung cấp phân tích cú pháp XML được 'cắm vào' thông qua các thuộc tính hệ thống nổi tiếng, được sử dụng để xây dựng các cá thể mới thông qua sự phản chiếu.

Và cuối cùng, ví dụ toàn diện nhất là Mùa xuân trong đó sử dụng sự phản chiếu để tạo ra hạt đậu của nó, và cho việc sử dụng nhiều proxy


30
2017-09-01 09:30





Không phải mọi ngôn ngữ đều hỗ trợ sự phản chiếu nhưng các nguyên tắc thường giống nhau trong các ngôn ngữ hỗ trợ nó.

Phản ánh là khả năng "phản ánh" về cấu trúc chương trình của bạn. Hoặc cụ thể hơn. Để xem các đối tượng và các lớp mà bạn có và lập trình lấy lại thông tin về các phương thức, các trường và các giao diện mà chúng thực hiện. Bạn cũng có thể xem những thứ như chú thích.

Đó là hữu ích trong rất nhiều tình huống. Ở khắp mọi nơi bạn muốn có thể tự động cắm các lớp vào mã của bạn. Những người lập bản đồ quan hệ của Lot sử dụng sự phản chiếu để có thể khởi tạo các đối tượng từ cơ sở dữ liệu mà không biết trước những đối tượng mà họ sẽ sử dụng. Kiến trúc trình cắm thêm là một nơi khác mà sự phản chiếu hữu ích. Có khả năng tự động tải mã và xác định xem có các loại có triển khai giao diện phù hợp để sử dụng làm plugin không quan trọng trong các tình huống đó.


27
2017-09-01 08:50



Tôi cần phải khởi tạo các đối tượng dựa trên dữ liệu có trong DB. Tôi tin rằng đây là những gì bạn đang nói về. Mã mẫu sẽ giúp tôi rất nhiều. Cảm ơn trước. - Atom


Sự phản chiếu cho phép khởi tạo các đối tượng mới, gọi các phương thức, và nhận / thiết lập các hoạt động trên các biến lớp một cách năng động vào thời gian chạy mà không cần phải biết trước về việc thực thi nó.

Class myObjectClass = MyObject.class;
Method[] method = myObjectClass.getMethods();

//Here the method takes a string parameter if there is no param, put null.
Method method = aClass.getMethod("method_name", String.class); 

Object returnValue = method.invoke(null, "parameter-value1");

Trong ví dụ trên, tham số null là đối tượng bạn muốn gọi phương thức. Nếu phương thức là tĩnh bạn cung cấp null. Nếu phương thức không tĩnh, thì khi gọi bạn cần cung cấp một cá thể MyObject hợp lệ thay vì null.

Reflection cũng cho phép bạn truy cập vào private member / methods của một class:

public class A{

  private String str= null;

  public A(String str) {
  this.str= str;
  }
}

.

A obj= new A("Some value");

Field privateStringField = A.class.getDeclaredField("privateString");

//Turn off access check for this field
privateStringField.setAccessible(true);

String fieldValue = (String) privateStringField.get(obj);
System.out.println("fieldValue = " + fieldValue);
  • Để kiểm tra các lớp (cũng biết là nội suy) bạn không cần phải nhập gói phản chiếu (java.lang.reflect). Siêu dữ liệu lớp có thể được truy cập thông qua java.lang.Class.

Reflection là một API rất mạnh nhưng nó có thể làm chậm ứng dụng nếu được sử dụng quá mức, vì nó giải quyết tất cả các kiểu khi chạy.


26
2017-07-08 16:12



Đây là bài viết đã giúp tôi nhiều nhất trong phần trả lời cảm ơn bạn Nikhil - Enoy


Thí dụ :
Lấy ví dụ một ứng dụng từ xa cung cấp cho ứng dụng của bạn một đối tượng mà bạn có được bằng cách sử dụng các phương thức API của chúng. Bây giờ dựa trên đối tượng bạn có thể cần để thực hiện một số loại tính toán.
Nhà cung cấp đảm bảo rằng đối tượng có thể có 3 loại và chúng tôi cần thực hiện tính toán dựa trên loại đối tượng nào.
Vì vậy, chúng tôi có thể thực hiện trong 3 lớp, mỗi lớp chứa một logic khác nhau. Rõ ràng là thông tin đối tượng có sẵn trong thời gian chạy nên bạn không thể mã tĩnh để thực hiện tính toán do đó sự phản chiếu được sử dụng để khởi tạo đối tượng của lớp mà bạn yêu cầu để thực hiện tính toán dựa trên đối tượng nhận được từ nhà cung cấp.


18
2018-06-22 15:35



Tôi cần một cái gì đó tương tự .. Một ví dụ sẽ giúp tôi rất nhiều như tôi mới để khái niệm phản ánh .. - Atom
Tôi bối rối: bạn không thể sử dụng instanceof để xác định loại đối tượng trong thời gian chạy? - ndm13