前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >反射基础之Field

反射基础之Field

作者头像
代码拾遗
发布2018-07-24 15:56:04
8020
发布2018-07-24 15:56:04
举报
文章被收录于专栏:代码拾遗代码拾遗

java.lang.reflect.Field 类的方法可以查询字段的信息。比如:名字,类型,修饰符和注解。同样也有方法可以动态访问和修改字段的值。

获取字段的类型

下面这个例子是如何获取字段的类型和泛型类型:

代码语言:javascript
复制
import java.lang.reflect.Field;
import java.util.List;

public class FieldSpy<T> {
    public boolean[][] b = {{ false, false }, { true, true } };
    public String name  = "Alice";
    public List<Integer> list;
    public T val;

    public static void main(String... args) {
    try {
        Class<?> c = Class.forName(args[0]);
        Field f = c.getField(args[1]);
        System.out.format("Type: %s%n", f.getType());
        System.out.format("GenericType: %s%n", f.getGenericType());

        // production code should handle these exceptions more gracefully
    } catch (ClassNotFoundException x) {
        x.printStackTrace();
    } catch (NoSuchFieldException x) {
        x.printStackTrace();
    }
    }
}

运行结果:

代码语言:javascript
复制
java FieldSpy FieldSpy b
Type: class [[Z
GenericType: class [[Z
$ java FieldSpy FieldSpy name
Type: class java.lang.String
GenericType: class java.lang.String
$ java FieldSpy FieldSpy list
Type: interface java.util.List
GenericType: java.util.List<java.lang.Integer>
$ java FieldSpy FieldSpy val
Type: class java.lang.Object
GenericType: T

b的类型是一个boolean的二维数组,类型名师通过Class.getName()获取的 val的类型是 java.lang.Object 因为Java的类型擦除在编译是会移出泛型信息 Field.getGenericType() 或检查类文件中的信息,如果没有提供则使用Field.getType()。

查询解析字段修饰符

可以修饰字段的修饰符:

  • 访问修饰符:public,protected,private。
  • 影响运行状态行为的:transient,volatile
  • 静态修饰符: static
  • 不可变修饰:final
  • 注解

方法Field.getModifiers() 可以返回其修饰符,修饰符的定义在java.lang.reflect.Modifier。 通过Field.isSynthetic() 检测是否是合成域(synthetic),Field.isEnumConstant()检测是否是枚举常量

代码语言:javascript
复制
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import static java.lang.System.out;

enum Spy { BLACK , WHITE }

public class FieldModifierSpy {
    volatile int share;
    int instance;
    class Inner {}

    public static void main(String... args) {
    try {
        Class<?> c = Class.forName(args[0]);
        int searchMods = 0x0;
        for (int i = 1; i < args.length; i++) {
        searchMods |= modifierFromString(args[i]);
        }

        Field[] flds = c.getDeclaredFields();
        out.format("Fields in Class '%s' containing modifiers:  %s%n",
               c.getName(),
               Modifier.toString(searchMods));
        boolean found = false;
        for (Field f : flds) {
        int foundMods = f.getModifiers();
        // Require all of the requested modifiers to be present
        if ((foundMods & searchMods) == searchMods) {
            out.format("%-8s [ synthetic=%-5b enum_constant=%-5b ]%n",
                   f.getName(), f.isSynthetic(),
                   f.isEnumConstant());
            found = true;
        }
        }

        if (!found) {
        out.format("No matching fields%n");
        }

        // production code should handle this exception more gracefully
    } catch (ClassNotFoundException x) {
        x.printStackTrace();
    }
    }

    private static int modifierFromString(String s) {
    int m = 0x0;
    if ("public".equals(s))           m |= Modifier.PUBLIC;
    else if ("protected".equals(s))   m |= Modifier.PROTECTED;
    else if ("private".equals(s))     m |= Modifier.PRIVATE;
    else if ("static".equals(s))      m |= Modifier.STATIC;
    else if ("final".equals(s))       m |= Modifier.FINAL;
    else if ("transient".equals(s))   m |= Modifier.TRANSIENT;
    else if ("volatile".equals(s))    m |= Modifier.VOLATILE;
    return m;
    }
}

运行输出:

代码语言:javascript
复制
$ java FieldModifierSpy FieldModifierSpy volatile
Fields in Class 'FieldModifierSpy' containing modifiers:  volatile
share    [ synthetic=false enum_constant=false ]

$ java FieldModifierSpy Spy public
Fields in Class 'Spy' containing modifiers:  public
BLACK    [ synthetic=false enum_constant=true  ]
WHITE    [ synthetic=false enum_constant=true  ]

$ java FieldModifierSpy FieldModifierSpy\$Inner final
Fields in Class 'FieldModifierSpy$Inner' containing modifiers:  final
this$0   [ synthetic=true  enum_constant=false ]

$ java FieldModifierSpy Spy private static final
Fields in Class 'Spy' containing modifiers:  private static final
$VALUES  [ synthetic=true  enum_constant=false ]

注意,虽然一些字段在源代码中找不到,但是仍然会被列出,因为编译器会生成一些运行时需要的合成域。检测一个字段是否是合成域只需要执行Field.isSynthetic()即可。合成域一般是编译器独立的,但是一般使用this$0代表内部类。

获取和设置字段值

示例:

代码语言:javascript
复制
import java.lang.reflect.Field;
import java.util.Arrays;
import static java.lang.System.out;

enum Tweedle { DEE, DUM }

public class Book {
    public long chapters = 0;
    public String[] characters = { "Alice", "White Rabbit" };
    public Tweedle twin = Tweedle.DEE;

    public static void main(String... args) {
    Book book = new Book();
    String fmt = "%6S:  %-12s = %s%n";

    try {
        Class<?> c = book.getClass();

        Field chap = c.getDeclaredField("chapters");
        out.format(fmt, "before", "chapters", book.chapters);
        chap.setLong(book, 12);
        out.format(fmt, "after", "chapters", chap.getLong(book));

        Field chars = c.getDeclaredField("characters");
        out.format(fmt, "before", "characters",
               Arrays.asList(book.characters));
        String[] newChars = { "Queen", "King" };
        chars.set(book, newChars);
        out.format(fmt, "after", "characters",
               Arrays.asList(book.characters));

        Field t = c.getDeclaredField("twin");
        out.format(fmt, "before", "twin", book.twin);
        t.set(book, Tweedle.DUM);
        out.format(fmt, "after", "twin", t.get(book));

        // production code should handle these exceptions more gracefully
    } catch (NoSuchFieldException x) {
        x.printStackTrace();
    } catch (IllegalAccessException x) {
        x.printStackTrace();
    }
    }
}

相关输出:

代码语言:javascript
复制
$ java Book
BEFORE:  chapters     = 0
 AFTER:  chapters     = 12
BEFORE:  characters   = [Alice, White Rabbit]
 AFTER:  characters   = [Queen, King]
BEFORE:  twin         = DEE
 AFTER:  twin         = DUM

注意,使用反射设置字段的值的性能非常差,因为需要判断是否可以访问,而且并不会有编译优化。

问题排查
IllegalArgumentException due to Inconvertible Types

下面这个例子是将42设置到Integer的字段,如果非反射会自动的装箱,但是对于反射则需要自己来装箱:

代码语言:javascript
复制
import java.lang.reflect.Field;

public class FieldTrouble {
    public Integer val;

    public static void main(String... args) {
    FieldTrouble ft = new FieldTrouble();
    try {
        Class<?> c = ft.getClass();
        Field f = c.getDeclaredField("val");
        f.setInt(ft, 42);               // IllegalArgumentException

        // production code should handle these exceptions more gracefully
    } catch (NoSuchFieldException x) {
        x.printStackTrace();
    } catch (IllegalAccessException x) {
        x.printStackTrace();
    }
    }
}

运行结果:

代码语言:javascript
复制
$ java FieldTrouble
Exception in thread "main" java.lang.IllegalArgumentException: Can not set
  java.lang.Object field FieldTrouble.val to (long)42
        at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException
          (UnsafeFieldAccessorImpl.java:146)
        at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException
          (UnsafeFieldAccessorImpl.java:174)
        at sun.reflect.UnsafeObjectFieldAccessorImpl.setLong
          (UnsafeObjectFieldAccessorImpl.java:102)
        at java.lang.reflect.Field.setLong(Field.java:831)
        at FieldTrouble.main(FieldTrouble.java:11)

解决这类问题可以是使用Field.set(fit, new Integer(42))。可以通过 Class.isAssignableFrom()来检查类型。

NoSuchFieldException for Non-Public Fields

对于FiledSpy程序:

代码语言:javascript
复制
$ java FieldSpy java.lang.String count
java.lang.NoSuchFieldException: count
        at java.lang.Class.getField(Class.java:1519)
        at FieldSpy.main(FieldSpy.java:12)
IllegalAccessException when Modifying Final Fields

当访问private的时候,或者private没有设置setAccessible(true) 就会抛出这个异常:

代码语言:javascript
复制
import java.lang.reflect.Field;

public class FieldTroubleToo {
    public final boolean b = true;

    public static void main(String... args) {
    FieldTroubleToo ft = new FieldTroubleToo();
    try {
        Class<?> c = ft.getClass();
        Field f = c.getDeclaredField("b");
//      f.setAccessible(true);  // solution
        f.setBoolean(ft, Boolean.FALSE);   // IllegalAccessException

        // production code should handle these exceptions more gracefully
    } catch (NoSuchFieldException x) {
        x.printStackTrace();
    } catch (IllegalArgumentException x) {
        x.printStackTrace();
    } catch (IllegalAccessException x) {
        x.printStackTrace();
    }
    }
}

运行:

代码语言:javascript
复制
$ java FieldTroubleToo
java.lang.IllegalAccessException: Can not set final boolean field
  FieldTroubleToo.b to (boolean)false
        at sun.reflect.UnsafeFieldAccessorImpl.
          throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:55)
        at sun.reflect.UnsafeFieldAccessorImpl.
          throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:63)
        at sun.reflect.UnsafeQualifiedBooleanFieldAccessorImpl.setBoolean
          (UnsafeQualifiedBooleanFieldAccessorImpl.java:78)
        at java.lang.reflect.Field.setBoolean(Field.java:686)
        at FieldTroubleToo.main(FieldTroubleToo.java:12)
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-05-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 代码拾遗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 获取字段的类型
  • 查询解析字段修饰符
  • 获取和设置字段值
  • 问题排查
    • IllegalArgumentException due to Inconvertible Types
      • NoSuchFieldException for Non-Public Fields
      • IllegalAccessException when Modifying Final Fields
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档