“关于使用反射机制得到泛型的字段名的有关问题”的解答
“关于使用反射机制得到泛型的字段名的问题”的解答
一、问题
在之前的“关于使用反射机制得到泛型的字段名的问题”博客当中提到了问题,终于在看了《Effective Java》之后有所理解。
问题回顾
1、实体类
public class LSLSEntry { private String storeguid; private String checkguid; private String isstoprunning; public String _5801; public String _5802; public String _5803; public String _6001; public String _6002; public String _6003; public String _6101; public String _6201; public String _6301; public String _6401; public String _6501; public String _6502; public String _6601; public String _6602; public String _6701; public String _6702; public String _6703; public String _6704; public String _6801; public String _6802; public String _6803; public String _6804; public String _6805; public String _7201; public String _7401; public String _7402; public String _7403; public String _7404; public String _7601; public String _7701; public String _7702; public String _7703; public String _7704; public String _7705; public String _7706; public String _7707; public String _7708; public String _7709; public String _7801; public String _7802; public String _8001; public String _8101; public String _8102; public String _8103; public String _8104; public String _8105; public String _8106; public String _8107; public String _8108; public String _8109; public String _8110; public String _8111; public String _8112; public String _8201; public String _8301; public String _8401; public String _8402; public String _8403; public String _8404; public String getStoreguid() { return storeguid; } public void setStoreguid(String storeguid) { this.storeguid = storeguid; } public String getCheckguid() { return checkguid; } public void setCheckguid(String checkguid) { this.checkguid = checkguid; } public String getIsstoprunning() { return isstoprunning; } public void setIsstoprunning(String isstoprunning) { this.isstoprunning = isstoprunning; } }2、反射代码
import java.lang.reflect.Field; import org.junit.Test; public class TestReflect { @Test public void test() { //可以得到字段名 LSLSEntry entry = new LSLSEntry(); printValue(entry); } @Test public void test1() { //得不到字段名,为什么??? printValue(LSLSEntry.class); } @Test public void test2() { //得不到字段名,为什么?? PrintInfo2<LSLSEntry> info = new PrintInfo2<LSLSEntry>(LSLSEntry.class); info.printValue(); } @Test public void test3() { //可以得到字段名 LSLSEntry entry = new LSLSEntry(); PrintInfo<LSLSEntry> info = new PrintInfo<LSLSEntry>(entry); info.printValue(); } @Test public void test4() { //可以得到字段名 Field[] fields = LSLSEntry.class.getFields(); for (Field f : fields) { System.out.println(f.getName()); } } private <T> void printValue(T entry) { Field[] fields = entry.getClass().getFields(); for (Field f : fields) { System.out.println(f.getName()); } } private class PrintInfo<T> { T entry; public PrintInfo(T entry) { this.entry = entry; } private void printValue() { Field[] fields = entry.getClass().getFields(); for (Field f : fields) { System.out.println(f.getName()); } } } private class PrintInfo2<T> { Class<?> entry; public PrintInfo2(Class<?> entry) { this.entry = entry; } private void printValue() { Field[] fields = entry.getClass().getFields(); for (Field f : fields) { System.out.println(f.getName()); } } } }
二、问题解答
1、源代码当中参数传递的错误。
public PrintInfo(T entry) { this.entry = entry; }这种编写方式中T entry这样的参数表示的是引用的概念,同时这表示一个特定的类型。
类型推导
而这个类型的推导过程是由entry的类型所推导出来的。编译器通过检查方法参数的类型来计算类型参数的值。对于如
public static <E> Set<E> union(Set<E> s1, Set<E> s2);而言,编译器发现union的两个参数都是Set<String>类型,因此知道类型参数E必须为String。这个过程称作类型推导。
泛型特性
泛型在运行时会擦除掉的,因此T在运行时会被擦除掉,例如List<Integer>和List<String>在运行时都会解释为List。在向printValue传递*.class时传递的是类型信息,而非对具体实例的引用,而这时的类型信息被擦除掉了,entry成了Object。因此调用 entry.getClass().getFields();时得不到东西。这样的错误同时在只传递*.class的方法或类的调用当中出现,因此出现了以上错误。
2、为什么class<?>可以
private void printValue12(Class<?> clazz){ System.out.println(clazz.getName()); Field[] fields = clazz.getFields(); for (Field f : fields) { System.out.println(f.getName()); } } private <T> void printValue13(Class<T> clazz){ System.out.println(clazz.getName()); Field[] fields = clazz.getFields(); for (Field f : fields) { System.out.println(f.getName()); } }
两段代码可以呢?
原因还是在以上的解答,在这里,Class<?>存储的是类型信息,而传递来的.class正是类型信息。不管是Class<?>还是Class<T>都是类型信息,而T entry 这种方式传递的是引用。
三、总结
Class<?>是类型信息的概念。T entry 这种是引用的概念,在这种方式下有类型推导的过程。
正是因为对两者概念的不清楚导致了错误的产生。