Java反射与Golang反射简单对比

wx592a7561e9493 · · 585 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

参考链接: Java反射

0x00 前言 

前段时间学习了golang的基础语法,发现其反射的概念与Java的差别挺大,做个简单对比,记录一下。 为了测试Java反射,创建如下User类: 

public class User {

    public String username;

    private String password;

    

    public User() {}


    public User(String username, String password) {

        this.username = username;

        this.password = password;

    }


    public void publicMethod() {

        System.out.println("publicMethod is running ...");

    }


    private void privateMethod() {

        System.out.println("private is running ...");

    }

}

 

按照上面的User类,创建如下Golang结构: 

type User struct {

    Username string

    password string

}


func (u User) PublicMethod() {

    fmt.Println("PublicMethod is running ... ")

}


func (u User) privateMethod() {

    fmt.Println("privateMethod is running ...")

}

 

0x01 操作公有字段 

1.1 Java反射操作公有字段 

Java操作User类的公有字段username时,有如下几步: 1、获取User类的Class对象 2、获取username字段对应的Field对象 3、通过Field对象操作指定User对象的username字段值 

public static void main(String[] args) throws Exception {

        // 获取User类的Class对象,方法很多,这里采用Class.forName()方法

        Class clazz = Class.forName("hldf.reflecttest.User");

        // 获取User类中的username字段对应的Field对象

        Field field = clazz.getField("username");

        

        User user = new User("zhangsan", "123456");

        // 获取user对象的username字段值

        String username = (String) field.get(user);

        System.out.println(username); // 输出zhangsan


        // 设置user对象的username字段值为lisi

        field.set(user, "lisi");

        username = (String) field.get(user);

        System.out.println(username); // 输出lisi

    }

 

1.2 Golang反射操作公有字段 

Golang操作User结构的公有字段Username时,只需要通过reflect.ValueOf()方法获取User结构对应的reflect.Value对象来进行读取和修改操作即可: 

func main() {

    user := User{"zhangsan", "123456"}

    // 获取user对象对应的Value对象

    v := reflect.ValueOf(user)

    // 获取user对象的Username字段对应的Value对象

    field := v.FieldByName("Username")

    fmt.Println(field.String()) // 输出 zhangsan


    // 修改结构的字段时必须传入指针类型

    v = reflect.ValueOf(&user)

    // 调用FieldByName方法的Value对象不能是指针类型生成的,

    // 因此需先调用Elem方法获取指针指向的实际值

    field = v.Elem().FieldByName("Username")

    // 修改字段值

    field.SetString("lisi")

    fmt.Println(field.String()) // 输出 lisi

}

 

0x02 操作私有字段 

2.1 Java反射操作私有字段 

按照上面1.1中所述方式操作User类的私有字段password时是不会成功的,需做如下修改: 1、使用Class类的getDeclaredField方法获取私有字段password的Field对象 2、调用Field对象的setAccessible方法获取修改私有字段的权限 

public static void main(String[] args) throws Exception {

        Class clazz = Class.forName("hldf.reflecttest.User");

        // 获取私有字段对应的Field对象时需使用getDeclaredField方法

        Field field = clazz.getDeclaredField("password");


        // 获取修改私有字段password的权限

        field.setAccessible(true);


        User user = new User("zhangsan", "123456");

        String password = (String) field.get(user);

        System.out.println(password); // 输出123456


        field.set(user, "654321");

        password = (String) field.get(user);

        System.out.println(password); // 输出654321

    }

 

2.2 Golang反射操作私有字段 

使用上面Golang反射操作公有字段的方法操作私有字段时,读取操作可成功执行,但修改操作出现异常,且Golang没有提供类似Java的setAccessible方法来获取修改私有字段的权限的方法,因此Golang无法修改结构的私有字段: 

func main() {

    // 私有字段值可读取成功

    user := User{"zhangsan", "123456"}

    v := reflect.ValueOf(user)

    field := v.FieldByName("password")

    fmt.Println(field.String()) // 输出 123456


    // 修改私有字段时,抛出异常

    v = reflect.ValueOf(&user)

    field = v.Elem().FieldByName("password")

    field.SetString("654321") // 抛出异常,提示无法操作未导出的字段

    fmt.Println(field.String()) 

}

 

0x03 调用公有方法 

3.1 Java反射调用公有方法 

Java操作User类的公有方法publicMethod与上面操作公有字段username类似: 1、获取User类的Class对象 2、获取publicMethod方法对应的Method对象 3、通过Method对象操作指定User对象的publicMethod方法 

public static void main(String[] args) throws Exception {

        Class clazz = Class.forName("hldf.reflecttest.User");

        

        // 获取User类中publicMethod方法对应的Method对象

        Method publicMethod = clazz.getMethod("publicMethod");

        

        User user = new User();

        // 调用user对象的publicMethod方法

        publicMethod.invoke(user);

    }

 

3.2 Golang反射调用公有方法 

与上面操作结构的字段一样,这里同样只用到了reflect.Value对象: 

func main() {

    v := reflect.ValueOf(User{})

    

    // 获取user对象的PublicMethod方法对应的Value对象

    publicMethod := v.MethodByName("PublicMethod")

    

    // 调用方法,PublicMethod方法为无参方法,因此传入的参数值为空

    publicMethod.Call(nil)

}

 

0x04 调用私有方法 

4.1 Java反射调用私有方法 

同样,按照上面3.1中所述方法调用User类的私有方法privateMethod时是不会成功的,同样需做如下修改: 1、使用Class类的getDeclaredMethod方法获取私有方法privateMethod的Field对象 2、调用Method对象的setAccessible方法获取调用私有方法的权限 

public static void main(String[] args) throws Exception {

        Class clazz = Class.forName("hldf.reflecttest.User");

     

        Method privateMethod = clazz.getDeclaredMethod("privateMethod");


        // 获取调用私有方法的权限

        privateMethod.setAccessible(true);


        User user = new User();

        privateMethod.invoke(user);

    }

 

4.2 Golang反射调用私有方法 

与上面修改私有字段结果相同,Golang同样没有提供获取调用私有方法权限的方法: 

func main() {

    v := reflect.ValueOf(User{})

    

    privateMethod := v.MethodByName("privateMethod") // 获取的值为空

    

    privateMethod.Call(nil) //抛出异常

}

 

0x05 总结 

1、Java中的类(Class类)、字段(Field类)、方法(Method类)等均有相应的类来进行表示,因此需通过这些类来对对象进行操作,并且调用这些类的方法时需传入实际要被操作的对象值;而Golang中对字段、方法等的操作只需要使用reflect.ValueOf()方法,获取到实际要被操作的对象对应的reflect.Value对象,然后通过调用Value对象的方法即可直接操作对象。 2、Java提供了setAccessible方法来获取操作私有字段和私有方法的权限,而Golang未提供类似机制,因此Golang无法通过反射来操作私有字段和私有方法。



有疑问加站长微信联系(非本文作者)

本文来自:51CTO博客

感谢作者:wx592a7561e9493

查看原文:Java反射与Golang反射简单对比

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

585 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传