a

Câu hỏi Làm thế nào để tôi gọi một phương thức Java khi đưa ra tên phương thức như là một chuỗi?


Nếu tôi có hai biến:

Object obj;
String methodName = "getName";

Không biết lớp obj, làm thế nào tôi có thể gọi phương thức được xác định bởi methodName trên đó?

Phương thức được gọi không có tham số và String giá trị trả lại. nó là một getter cho một hạt Java.


571
2017-10-02 05:15


gốc


Sử dụng api phản chiếu hoặc dùng cá bơn - Peter Kelley


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


Mã hóa từ hông, nó sẽ là một cái gì đó như:

java.lang.reflect.Method method;
try {
  method = obj.getClass().getMethod(methodName, param1.class, param2.class, ..);
} catch (SecurityException e) { ... }
  catch (NoSuchMethodException e) { ... }

Các tham số xác định phương thức rất cụ thể mà bạn cần (nếu có một số quá tải có sẵn, nếu phương thức không có đối số, chỉ cho methodName).

Sau đó, bạn gọi phương thức đó bằng cách gọi

try {
  method.invoke(obj, arg1, arg2,...);
} catch (IllegalArgumentException e) { ... }
  catch (IllegalAccessException e) { ... }
  catch (InvocationTargetException e) { ... }

Một lần nữa, bỏ qua các đối số trong .invokenếu bạn không có. Nhưng vâng. Đọc về Phản ánh Java


833
2017-10-02 05:30



Có một chút khó chịu bởi thực tế là Java sử dụng loại tẩy xoá, nhưng biết rằng ít nhất nó có Reflection cổ vũ tôi một lần nữa: D Và bây giờ với lambdas trong Java 8 ngôn ngữ thực sự bắt kịp với tốc độ phát triển hiện đại. Chỉ có điều thiếu bây giờ là hỗ trợ bản địa cho getters và setters, hoặc các thuộc tính khi chúng được biết đến trong C #. - 7hi4g0
Không công bằng -1. Henrik có lẽ không ủng hộ các ngoại lệ bí mật và không viết bất cứ điều gì cho họ bởi vì anh ta chỉ đang cố gắng thể hiện sự phản chiếu. - drew
Thêm một cho thấy một số ngoại lệ tiềm năng. Nếu tôi đã viết điều này, nó sẽ là ... bắt (Exception e) {... - mikbanUtah
Tôi đã nhận "biến có thể chưa được khởi tạo" cho method trong method.invoke(obj, arg1, arg2,...);. một method = null; giải quyết vấn đề nhưng đề cập đến nó trong câu trả lời không phải là một ý tưởng tồi. - Amin
@ DeaMon1 Các phương thức Java không sử dụng "các mã thoát", nhưng nếu phương thức trả về bất kỳ thứ gì, invoke sẽ trả lại bất cứ điều gì nó trả lại. Nếu một ngoại lệ xảy ra khi chạy phương thức, ngoại lệ sẽ được bao bọc trong một InvocationTargetException. - ThePyroEagle


Sử dụng lời gọi phương thức từ sự phản chiếu:

Class<?> c = Class.forName("class name");
Method method = c.getDeclaredMethod("method name", parameterTypes);
method.invoke(objectToInvokeOn, params);

Ở đâu:

  • "class name" là tên của lớp
  • objectToInvokeOn là kiểu Object và là đối tượng bạn muốn gọi phương thức
  • "method name" là tên của phương thức bạn muốn gọi
  • parameterTypes thuộc loại Class[] và khai báo các tham số mà phương thức
  • params thuộc loại Object[] và khai báo các tham số được truyền cho phương thức

158
2017-10-02 05:21



Cool, tôi nghĩ rằng bạn đang phải với getDeclaredMethod (), nó có lẽ là 'an toàn hơn' so với getMethod () .. - brasskazoo
Sai rồi. Có, getDeclaredMethod không hoạt động với các phương thức riêng và được bảo vệ. NHƯNG: nó không hoạt động với các phương thức được định nghĩa trong các siêu lớp (các phương thức kế thừa). Vì vậy, nó phụ thuộc mạnh mẽ vào những gì bạn muốn làm. Trong nhiều trường hợp, bạn muốn nó hoạt động bất kể lớp chính xác mà phương thức được định nghĩa. - jrudolph
Và tôi nên đặt tệp "class" ở đâu? tốt nhất là giải thích cho Eclipse IDE - Mr.Hyde
@ Mr.Hyde trên con đường lớp. - Stijn de Witt


Đối với những người muốn có một ví dụ mã thẳng về phía trước trong Java 7:

Dog lớp học:

package com.mypackage.bean;

public class Dog {
    private String name;
    private int age;

    public Dog() {
        // empty constructor
    }

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    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;
    }

    public void printDog(String name, int age) {
        System.out.println(name + " is " + age + " year(s) old.");
    }
}

ReflectionDemo lớp học:

package com.mypackage.demo;

import java.lang.reflect.*;

public class ReflectionDemo {

    public static void main(String[] args) throws Exception {
        String dogClassName = "com.mypackage.bean.Dog";
        Class<?> dogClass = Class.forName(dogClassName); // convert string classname to class
        Object dog = dogClass.newInstance(); // invoke empty constructor

        String methodName = "";

        // with single parameter, return void
        methodName = "setName";
        Method setNameMethod = dog.getClass().getMethod(methodName, String.class);
        setNameMethod.invoke(dog, "Mishka"); // pass arg

        // without parameters, return string
        methodName = "getName";
        Method getNameMethod = dog.getClass().getMethod(methodName);
        String name = (String) getNameMethod.invoke(dog); // explicit cast

        // with multiple parameters
        methodName = "printDog";
        Class<?>[] paramTypes = {String.class, int.class};
        Method printDogMethod = dog.getClass().getMethod(methodName, paramTypes);
        printDogMethod.invoke(dog, name, 3); // pass args
    }
}

Đầu ra: Mishka is 3 year(s) old.


Bạn có thể gọi hàm tạo với các tham số theo cách này:

Constructor<?> dogConstructor = dogClass.getConstructor(String.class, int.class);
Object dog = dogConstructor.newInstance("Hachiko", 10);

Ngoài ra, bạn có thể xóa

String dogClassName = "com.mypackage.bean.Dog";
Class<?> dogClass = Class.forName(dogClassName);
Object dog = dogClass.newInstance();

và làm

Dog dog = new Dog();

Method method = Dog.class.getMethod(methodName, ...);
method.invoke(dog, ...);

Cách đọc được đề nghị:  Tạo các trường hợp lớp mới


64
2018-06-05 16:31



Câu trả lời hay nhất ở đây. Hoàn thành và súc tích - Reuben JaMes Aveño Gruta
Câu trả lời đúng nhất. - Dhara Patel


Phương thức này có thể được gọi như thế này. Ngoài ra còn có nhiều khả năng hơn (kiểm tra api phản chiếu), nhưng đây là cách đơn giản nhất:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.junit.Assert;
import org.junit.Test;

public class ReflectionTest {

    private String methodName = "length";
    private String valueObject = "Some object";

    @Test
    public void testGetMethod() throws SecurityException, NoSuchMethodException, IllegalArgumentException,
            IllegalAccessException, InvocationTargetException {
        Method m = valueObject.getClass().getMethod(methodName, new Class[] {});
        Object ret = m.invoke(valueObject, new Object[] {});
        Assert.assertEquals(11, ret);
    }



}

51
2017-10-02 05:33



1 cho câu trả lời duy nhất nhận ra rằng OP đã chỉ định "không có tham số" trong câu hỏi của anh ta (và vì đó là những gì tôi đang tìm kiếm). - John Fitzpatrick


Đầu tiên, đừng. Tránh loại mã này. Nó có xu hướng thực sự xấu mã và không an toàn quá (xem phần 6 của Nguyên tắc mã hóa an toàn cho Ngôn ngữ lập trình Java, phiên bản 2.0).

Nếu bạn phải làm điều đó, thích java.beans để phản ánh. Đậu kết thúc tốt đẹp phản ánh cho phép truy cập tương đối an toàn và thông thường.


16
2017-10-02 14:08



Tôi không đồng ý. Nó rất dễ dàng để viết mã như vậy để được an toàn và tôi đã làm như vậy trong nhiều ngôn ngữ. Ví dụ, người ta có thể tạo một tập hợp các phương thức cho phép, và chỉ cho phép một phương thức được gọi nếu nó có tên trong tập hợp. Thậm chí còn an toàn hơn (nhưng vẫn là đầu xương đơn giản) sẽ giới hạn từng phương thức được phép cho một trạng thái cụ thể và không cho phép phương thức được gọi trừ khi luồng / giao diện / người dùng / bất kỳ điều gì phù hợp với tiêu chí đó. - JSON


Để hoàn thành câu trả lời của đồng nghiệp, bạn có thể muốn chú ý đến:

  • các cuộc gọi tĩnh hoặc cá thể (trong một trường hợp, bạn không cần một cá thể của lớp, trong trường hợp khác, bạn có thể cần phải dựa vào một hàm tạo mặc định hiện có có thể có hoặc không ở đó)
  • cuộc gọi phương thức công cộng hoặc không công khai (cho cuộc gọi thứ hai,bạn cần gọi setAccessible trên phương thức trong khối doPrivileged, khác findbugs sẽ không được hạnh phúc)
  • đóng gói vào một ngoại lệ ứng dụng dễ quản lý hơn nếu bạn muốn vứt bỏ nhiều ngoại lệ hệ thống java (do đó, CCException trong mã bên dưới)

Đây là một mã java1.4 cũ có tính đến các điểm đó:

/**
 * Allow for instance call, avoiding certain class circular dependencies. <br />
 * Calls even private method if java Security allows it.
 * @param aninstance instance on which method is invoked (if null, static call)
 * @param classname name of the class containing the method 
 * (can be null - ignored, actually - if instance if provided, must be provided if static call)
 * @param amethodname name of the method to invoke
 * @param parameterTypes array of Classes
 * @param parameters array of Object
 * @return resulting Object
 * @throws CCException if any problem
 */
public static Object reflectionCall(final Object aninstance, final String classname, final String amethodname, final Class[] parameterTypes, final Object[] parameters) throws CCException
{
    Object res;// = null;
    try {
        Class aclass;// = null;
        if(aninstance == null)
        {
            aclass = Class.forName(classname);
        }
        else
        {
            aclass = aninstance.getClass();
        }
        //Class[] parameterTypes = new Class[]{String[].class};
    final Method amethod = aclass.getDeclaredMethod(amethodname, parameterTypes);
        AccessController.doPrivileged(new PrivilegedAction() {
    public Object run() {
                amethod.setAccessible(true);
                return null; // nothing to return
            }
        });
        res = amethod.invoke(aninstance, parameters);
    } catch (final ClassNotFoundException e) {
        throw new CCException.Error(PROBLEM_TO_ACCESS+classname+CLASS, e);
    } catch (final SecurityException e) {
        throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_SECURITY_ISSUE, e);
    } catch (final NoSuchMethodException e) {
        throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_NOT_FOUND, e);
    } catch (final IllegalArgumentException e) {
        throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_ILLEGAL_ARGUMENTS+String.valueOf(parameters)+GenericConstants.CLOSING_ROUND_BRACKET, e);
    } catch (final IllegalAccessException e) {
        throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_ACCESS_RESTRICTION, e);
    } catch (final InvocationTargetException e) {
    throw new CCException.Error(PROBLEM_TO_ACCESS+classname+GenericConstants.HASH_DIESE+ amethodname + METHOD_INVOCATION_ISSUE, e);
    } 
    return res;
}

12
2017-10-02 05:51





//Step1 - Using string funClass to convert to class
String funClass = "package.myclass";
Class c = Class.forName(funClass);

//Step2 - instantiate an object of the class abov
Object o = c.newInstance();
//Prepare array of the arguments that your function accepts, lets say only one string here
Class[] paramTypes = new Class[1];
paramTypes[0]=String.class;
String methodName = "mymethod";
//Instantiate an object of type method that returns you method name
 Method m = c.getDeclaredMethod(methodName, paramTypes);
//invoke method with actual params
m.invoke(o, "testparam");

10
2018-04-30 05:46