前段时间学习了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无法通过反射来操作私有字段和私有方法。