2010-02-09 34 views
5

Là một phần của việc phát triển một ScriptEngine nhỏ, tôi gọi một cách phản ánh các phương thức java. Một cuộc gọi của công cụ script cho tôi đối tượng tên phương thức và một mảng các đối số. Để gọi phương thức, tôi đã cố gắng giải quyết nó bằng một cuộc gọi tới Class.getMethod (tên, kiểu đối số).
Tuy nhiên, điều này chỉ hoạt động khi các lớp của đối số và các lớp được mong đợi theo Phương thức giống nhau.Gọi phương thức lắp gần nhất

Object o1 = new Object(); 
Object out = System.out; 
//Works as System.out.println(Object) is defined 
Method ms = out.getClass().getMethod("println",o1.getClass()); 
Object o2 = new Integer(4); 
//Does not work as System.out.println(Integer) is not defined 
Method mo = out.getClass().getMethod("println",o2.getClass()); 

Tôi muốn biết nếu có cách "đơn giản" để có phương pháp phù hợp, nếu có thể phù hợp nhất với các loại đối số hoặc nếu tôi phải thực hiện điều này.

phù hợp gần nhất sẽ là:

Object o1 = new Integer(1); 
Object o2 = new String(""); 
getMethod(name, o1.getClass())//println(Object) 
getMethod(name, o2.getClass())//println(String) 

Cập nhật:
Để làm rõ những gì tôi cần: The Script Engine là một dự án nhỏ tôi viết trong thời gian rảnh rỗi vì vậy không có quy tắc strikt Tôi có để làm theo. Vì vậy, tôi nghĩ rằng việc chọn các phương thức được gọi từ Engine giống như cách trình biên dịch java chọn các phương thức tại thời gian biên dịch chỉ với kiểu động và không phải kiểu tĩnh của Object sẽ hoạt động (có hoặc không có autoboxing)
Đây là những gì tôi đầu tiên hy vọng rằng Class.getMethod() sẽ giải quyết. Nhưng Class.getMethod() đòi hỏi các lớp giống như các kiểu đối số chính xác như phương thức khai báo, sử dụng một lớp con sẽ dẫn đến một ngoại lệ không có phương thức như vậy. Điều này có thể xảy ra vì lý do tốt, nhưng làm cho phương pháp vô dụng đối với tôi, vì tôi không biết trước các loại đối số nào sẽ phù hợp.
Một thay thế sẽ là gọi Class.getMethods() và lặp qua mảng được trả về và cố gắng tìm phương thức phù hợp. Tuy nhiên, điều này sẽ phức tạp nếu tôi không muốn sử dụng phương pháp "tốt" đầu tiên mà tôi gặp phải, vì vậy tôi hy vọng rằng sẽ có một giải pháp hiện có ít nhất là xử lý:

  • gần nhất: Nếu arg.getClass() == lớp con và phương pháp m (lớp cha), m (subclass) sau đó gọi m (subclass)
  • tranh cãi biến: System.out.printf (string, string ...)

Hỗ trợ tự động hóa cũng sẽ thú vị.
Nếu một cuộc gọi không thể giải quyết, nó có thể ném một ngoại lệ (ma (String, Object), ma (Object, String), args = String, String)
(Nếu bạn thực hiện nó đến đây, cảm ơn bạn đã dành thời gian để đọc nó :-))

+0

đầu tiên xác định quy tắc nghiêm ngặt cho "gần gũi", sau đó thực hiện một cách chi tiết. – Bozho

+0

chỉ là những gì tôi đã tự hỏi, quá. Nhưng đối với tất cả những người trả lời chính xác về mặt ngữ nghĩa, "Xác định gần nhất", tôi có một cách dễ dàng: cái mà Java bình thường sẽ chọn (tôi đoán là khi tải lớp). –

Trả lời

2

Như những người khác đã chỉ ra không có phương pháp tiêu chuẩn nào thực hiện điều này, vì vậy bạn sẽ phải thực hiện thuật toán phân giải quá tải của riêng bạn.

Nó có lẽ sẽ làm cho tinh thần để làm theo quy tắc giải quyết tình trạng quá tải javac của càng sát càng tốt:
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#292575
Bạn có thể bỏ qua Generics cho một ngôn ngữ kịch bản tự động, đánh máy, nhưng bạn vẫn có thể hưởng lợi từ việc bridge methods rằng trình biên dịch tạo tự động.

Một số cạm bẫy để xem ra cho:

  • Class.isAssignableFrom không biết về chuyển đổi nguyên thủy tự động mở rộng, bởi vì đây là những đường cú pháp thực hiện trong trình biên dịch; Chúng không xuất hiện trong hệ thống phân cấp VM hoặc lớp. ví dụ. int.class.isAssignableFrom(short.class) trả về false.
  • Tương tự Class.isAssignableFrom không biết về tự động đấm bốc. Integer.class.isAssignableFrom(int.class) trả lại false.
  • Class.isInstanceClass.cast lấy một đối số Object; Bạn không thể truyền các giá trị nguyên thủy cho chúng. Họ cũng trả về một Object, vì vậy họ không thể được sử dụng cho unboxing ((int) new Integer(42) là hợp pháp ở nguồn Java nhưng int.class.cast(new Integer(42)) ném một ngoại lệ.)
1

AFAIK, không có cách nào đơn giản để làm điều này. Chắc chắn, không có gì trong thư viện lớp Java chuẩn để làm điều này.

Vấn đề là không có câu trả lời "đúng". Bạn cần phải xem xét tất cả các trường hợp sử dụng của bạn, quyết định "phương thức đúng" sẽ là gì và triển khai mã phản chiếu của bạn cho phù hợp.

2

Tôi khuyên bạn nên sử dụng getMethods(). Nó trả về một mảng của tất cả các phương thức công cộng (Method[]).

Điều quan trọng nhất ở đây là:
" Nếu lớp tuyên bố nhiều phương pháp thành viên công cộng với các loại tham số giống nhau, tất cả chúng đều có trong mảng được trả về. "

gì sau đó bạn sẽ cần phải làm là để sử dụng kết quả trong mảng này để xác định mà một trong số đó (nếu có) là những trận đấu gần nhất. Vì những gì phù hợp nhất nên phụ thuộc rất nhiều vào yêu cầu của bạn và ứng dụng cụ thể, nó có ý nghĩa để mã nó cho mình.


Mẫu mã minh họa một cách tiếp cận như thế nào bạn có thể đi về việc này:

public Method getMethod(String methodName, Class<?> clasz) 
{ 
    try 
    { 
     Method[] methods = clasz.getMethods(); 
     for (Method method : methods) 
     { 
      if (methodName.equals(method.getName())) 
      { 
       Class<?>[] params = method.getParameterTypes(); 
       if (params.length == 1) 
       { 
        Class<?> param = params[0]; 
        if ((param == int.class) || (param == float.class) || (param == float.class)) 
        { 
         //method.invoke(object, value); 
         return method; 
        } 
        else if (param.isAssignableFrom(Number.class)) 
        { 
         return method; 
        } 
        //else if (...) 
        //{ 
        // ... 
        //} 
       } 
      } 
     } 
    } 
    catch (Exception e) 
    { 
     //some handling 
    } 
    return null; 
} 

Trong ví dụ này, phương pháp getMethod(String, Class<?>) sẽ trả về một phương pháp mà chỉ với một tham số mà là một int, float , double hoặc siêu lớp của Number.

Đây là triển khai thô sơ - Trả về phương thức đầu tiên phù hợp với hóa đơn. Bạn sẽ cần phải mở rộng nó để tạo danh sách tất cả các phương thức phù hợp, và sau đó sắp xếp chúng theo một số loại tiêu chí và trả lại phương thức phù hợp nhất.

Sau đó bạn có thể mang nó hơn nữa bằng cách tạo ra tổng quát hơn phương pháp getMethod(String, Class<?>), để xử lý nhiều tình huống "trận đấu gần" có thể, và thậm chí có thể nhiều hơn một paramter

HTH


Chỉnh sửa: Như @finnw đã chỉ ra, hãy cẩn thận khi sử dụng Class#isAssignableFrom(Class<?> cls), do những hạn chế của nó, như tôi có trong mã mẫu của tôi, kiểm tra các nguyên thủy riêng biệt với các đối tượng Number.

+1

@ bguiz, giải pháp tốt đẹp. Chỉ muốn thêm rằng bạn có thể kiểm tra 'Class.isPrimitive()' và 'Class.isArray()' để làm việc xung quanh một số hạn chế của isAssignableFrom(). @ finnw của câu trả lời quá. – bojangle

Các vấn đề liên quan