发现class成员
Class中提供了用于访问字段,方法和构造函数的两类方法:枚举这些成员的方法和搜索特定成员的方法。与直接在类上声明的成员的访问方法相比,还有很多方法与在超interface和超类中搜索继承成员的方法不同。下表总结了所有成员定位方法及其 Feature。
用于定位字段的类方法
Class API | 成员名单? | Inherited members? | Private members? |
---|---|---|---|
getDeclaredField() | no | no | yes |
getField() | no | yes | no |
getDeclaredFields() | yes | no | yes |
getFields() | yes | yes | no |
用于定位方法的类方法
Class API | 成员名单? | Inherited members? | Private members? |
---|---|---|---|
getDeclaredMethod() | no | no | yes |
getMethod() | no | yes | no |
getDeclaredMethods() | yes | no | yes |
getMethods() | yes | yes | no |
用于定位构造函数的类方法
Class API | 成员名单? | Inherited members? | Private members? |
---|---|---|---|
getDeclaredConstructor() | no | N/A1 | yes |
getConstructor() | no | N/A1 | no |
getDeclaredConstructors() | yes | N/A1 | yes |
getConstructors() | yes | N/A1 | no |
1 构造函数未继承。
给定一个类名并指出感兴趣的成员,ClassSpy示例使用get*s()
方法来确定所有公共元素(包括任何继承的元素)的列表。
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Member;
import static java.lang.System.out;
enum ClassMember { CONSTRUCTOR, FIELD, METHOD, CLASS, ALL }
public class ClassSpy {
public static void main(String... args) {
try {
Class<?> c = Class.forName(args[0]);
out.format("Class:%n %s%n%n", c.getCanonicalName());
Package p = c.getPackage();
out.format("Package:%n %s%n%n",
(p != null ? p.getName() : "-- No Package --"));
for (int i = 1; i < args.length; i++) {
switch (ClassMember.valueOf(args[i])) {
case CONSTRUCTOR:
printMembers(c.getConstructors(), "Constructor");
break;
case FIELD:
printMembers(c.getFields(), "Fields");
break;
case METHOD:
printMembers(c.getMethods(), "Methods");
break;
case CLASS:
printClasses(c);
break;
case ALL:
printMembers(c.getConstructors(), "Constuctors");
printMembers(c.getFields(), "Fields");
printMembers(c.getMethods(), "Methods");
printClasses(c);
break;
default:
assert false;
}
}
// production code should handle these exceptions more gracefully
} catch (ClassNotFoundException x) {
x.printStackTrace();
}
}
private static void printMembers(Member[] mbrs, String s) {
out.format("%s:%n", s);
for (Member mbr : mbrs) {
if (mbr instanceof Field)
out.format(" %s%n", ((Field)mbr).toGenericString());
else if (mbr instanceof Constructor)
out.format(" %s%n", ((Constructor)mbr).toGenericString());
else if (mbr instanceof Method)
out.format(" %s%n", ((Method)mbr).toGenericString());
}
if (mbrs.length == 0)
out.format(" -- No %s --%n", s);
out.format("%n");
}
private static void printClasses(Class<?> c) {
out.format("Classes:%n");
Class<?>[] clss = c.getClasses();
for (Class<?> cls : clss)
out.format(" %s%n", cls.getCanonicalName());
if (clss.length == 0)
out.format(" -- No member interfaces, classes, or enums --%n");
out.format("%n");
}
}
这个例子比较紧凑。但是,printMembers()
方法有点尴尬,因为自从最早的反射实现以来就存在java.lang.reflect.Memberinterface,并且在引入泛型时不能将其修改为包括更有用的getGenericString()
方法。唯一的选择是如图所示进行测试和转换,用printConstructors()
,printFields()
和printMethods()
替换此方法,或者对Member.getName()的相对备用结果感到满意。
输出 samples 及其解释如下。用户 Importing 以斜体显示。
$ java ClassSpy java.lang.ClassCastException CONSTRUCTOR
Class:
java.lang.ClassCastException
Package:
java.lang
Constructor:
public java.lang.ClassCastException()
public java.lang.ClassCastException(java.lang.String)
由于没有继承构造函数,因此找不到在直接超类RuntimeException和其他超类中定义的异常链接机制构造函数(带有Throwable参数的异常)。
$ java ClassSpy java.nio.channels.ReadableByteChannel METHOD
Class:
java.nio.channels.ReadableByteChannel
Package:
java.nio.channels
Methods:
public abstract int java.nio.channels.ReadableByteChannel.read
(java.nio.ByteBuffer) throws java.io.IOException
public abstract void java.nio.channels.Channel.close() throws
java.io.IOException
public abstract boolean java.nio.channels.Channel.isOpen()
interfacejava.nio.channels.ReadableByteChannel定义read()。其余方法是从superinterface继承的。通过将get*s()
替换为getDeclared*s()
,可以轻松地修改此代码以仅列出那些在类中实际声明的方法。
$ java ClassSpy ClassMember FIELD METHOD
Class:
ClassMember
Package:
-- No Package --
Fields:
public static final ClassMember ClassMember.CONSTRUCTOR
public static final ClassMember ClassMember.FIELD
public static final ClassMember ClassMember.METHOD
public static final ClassMember ClassMember.CLASS
public static final ClassMember ClassMember.ALL
Methods:
public static ClassMember ClassMember.valueOf(java.lang.String)
public static ClassMember[] ClassMember.values()
public final int java.lang.Enum.hashCode()
public final int java.lang.Enum.compareTo(E)
public int java.lang.Enum.compareTo(java.lang.Object)
public final java.lang.String java.lang.Enum.name()
public final boolean java.lang.Enum.equals(java.lang.Object)
public java.lang.String java.lang.Enum.toString()
public static <T> T java.lang.Enum.valueOf
(java.lang.Class<T>,java.lang.String)
public final java.lang.Class<E> java.lang.Enum.getDeclaringClass()
public final int java.lang.Enum.ordinal()
public final native java.lang.Class<?> java.lang.Object.getClass()
public final native void java.lang.Object.wait(long) throws
java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws
java.lang.InterruptedException
public final void java.lang.Object.wait() hrows java.lang.InterruptedException
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
在这些结果的字段部分中,列出了枚举常量。虽然这些是技术上的领域,但将它们与其他领域区分开来可能很有用。为此,可以将本示例修改为使用java.lang.reflect.Field.isEnumConstant()。本章后面的EnumSpy示例Examining Enums包含一个可能的实现。
在输出的方法部分中,观察到方法名称包括声明类的名称。因此,toString()
方法由Enum实现,而不是从Object继承。可以通过使用Field.getDeclaringClass()修改代码以使其更加明显。以下片段说明了潜在解决方案的一部分。
if (mbr instanceof Field) {
Field f = (Field)mbr;
out.format(" %s%n", f.toGenericString());
out.format(" -- declared in: %s%n", f.getDeclaringClass());
}