在实践中了解Java反射机制应用
引言
Java反射机制是一个非常强大的功能,在很多大型项目比如Spring,Mybatis都可以看见反射的身影。通过反射机制我们可以在运行期间获取对象的类型信息,利用这一特性我们可以实现工厂模式和代理模式等设计模式,同时也可以解决Java泛型擦除等令人苦恼的问题。本文我们就从实际应用的角度出发,来应用一下Java的反射机制。
反射基础
p.s:本文需要读者对反射机制的API有一定程度的了解,如果之前没有接触过的话,建议先看一下官方文档的QuickStart。
在应用反射机制之前,首先我们先来看一下如何获取一个对象对应的反射类Class,在Java中我们有三种方法可以获取一个对象的反射类。
通过getClass方法
在Java中,每一个Object都有一个getClass方法,通过getClass方法我们可以获取到这个对象对应的反射类:
Strings="ziwenxie"; Class>c=s.getClass();
通过forName方法
我们也可以调用Class类的静态方法forName:
Class>c=Class.forName("java.lang.String");
使用.class
或者我们也可以直接使用.class:
Class>c=String.class;
获取类型信息
在文章开头我们就提到反射的一大好处就是可以允许我们在运行期间获取对象的类型信息,下面我们通过一个例子来具体看一下。
首先我们在typeinfo.interfacea包下面新建一个接口A:
packagetypeinfo.interfacea; publicinterfaceA{voidf();}
接着我们在typeinfo.packageaccess包下面新建一个接口C,接口C继承自接口A,并且我们还另外创建了几个用于测试的方法,注意下面几个方法的权限都是不同的。
packagetypeinfo.packageaccess; importtypeinfo.interfacea.A; classCimplementsA{ publicvoidf(){System.out.println("publicC.f()");} publicvoidg(){System.out.println("publicC.g()");} protectedvoidv(){System.out.println("protectedC.v()");} voidu(){System.out.println("packageC.u()");} privatevoidw(){System.out.println("privateC.w()");} } publicclassHiddenC{ publicstaticAmakeA(){returnnewC();} }
在callHiddenMethod()方法中我们用到了几个新的API,其中getDeclaredMethod()根据方法名用于获取Class类指代对象的某个方法,然后我们通过调用invoke()方法传入实际的对象就可以触发对象的相关方法:
packagetypeinfo; importtypeinfo.interfacea.A; importtypeinfo.packageaccess.HiddenC; importjava.lang.reflect.Method; publicclassHiddenImplementation{ publicstaticvoidmain(String[]args)throwsException{ Aa=HiddenC.makeA(); a.f(); System.out.println(a.getClass().getName()); //Oops!Reflectionstillallowsustocallg(): callHiddenMethod(a,"g"); //Andevenmethodsthatarelessaccessible! callHiddenMethod(a,"u"); callHiddenMethod(a,"v"); callHiddenMethod(a,"w"); } staticvoidcallHiddenMethod(Objecta,StringmethodName)throwsException{ Methodg=a.getClass().getDeclaredMethod(methodName); g.setAccessible(true); g.invoke(a); } }
从输出结果我们可以看出来,不管是public,default,protect还是pricate方法,通过反射类我们都可以自由调用。当然这里我们只是为了显示反射的强大威力,在实际开发中这种技巧还是不提倡。
publicC.f() typeinfo.packageaccess.C publicC.g() packageC.u() protectedC.v() privateC.w()
应用实践
我们有下面这样一个业务场景,我们有一个泛型集合类List
为了实现我们上面的例子,我们先来定义几个类:
publicclassPetextendsIndividual{ publicPet(Stringname){super(name);} publicPet(){super();} } publicclassCatextendsPet{ publicCat(Stringname){super(name);} publicCat(){super();} } publicclassDogextendsPet{ publicDog(Stringname){super(name);} publicDog(){super();} } publicclassEgyptianMauextendsCat{ publicEgyptianMau(Stringname){super(name);} publicEgyptianMau(){super();} } publicclassMuttextendsDog{ publicMutt(Stringname){super(name);} publicMutt(){super();} }
上面的Pet类继承自Individual,Individual类的的实现稍微复杂一点,我们实现了Comparable接口,重新自定义了类的比较规则,如果不是很明白的话,也没有关系,我们已经将它抽象出来了,所以不理解实现原理也没有关系。
publicclassIndividualimplementsComparable{ privatestaticlongcounter=0; privatefinallongid=counter++; privateStringname;//nameisoptional publicIndividual(Stringname){this.name=name;} publicIndividual(){} publicStringtoString(){ returngetClass().getSimpleName()+(name==null?"":""+name); } publiclongid(){returnid;} publicbooleanequals(Objecto){ returnoinstanceofIndividual&&id==((Individual)o).id; } publicinthashCode(){ intresult=17; if(name!=null){ result=37*result+name.hashCode(); } result=37*result+(int)id; returnresult; } publicintcompareTo(Individualarg){ //Comparebyclassnamefirst: Stringfirst=getClass().getSimpleName(); StringargFirst=arg.getClass().getSimpleName(); intfirstCompare=first.compareTo(argFirst); if(firstCompare!=0){ returnfirstCompare; } if(name!=null&&arg.name!=null){ intsecendCompare=name.compareTo(arg.name); if(secendCompare!=0){ returnsecendCompare; } } return(arg.id 下面创建了一个抽象类PetCreator,以后我们通过调用arrayList()方法便可以直接获取相关Pet类的集合。这里使用到了我们上面没有提及的newInstance()方法,它会返回Class类所真正指代的类的实例,这是什么意思呢?比如说声明newDog().getClass().newInstance()和直接newDog()是等价的。
publicabstractclassPetCreator{ privateRandomrand=newRandom(47); //TheListofthedifferentgetTypesofPettocreate: publicabstractList>getTypes(); publicPetrandomPet(){ //CreateonerandomPet intn=rand.nextInt(getTypes().size()); try{ returngetTypes().get(n).newInstance(); }catch(InstantiationExceptione){ thrownewRuntimeException(e); }catch(IllegalAccessExceptione){ thrownewRuntimeException(e); } } publicPet[]createArray(intsize){ Pet[]result=newPet[size]; for(inti=0;i arrayList(intsize){ ArrayList result=newArrayList (); Collections.addAll(result,createArray(size)); returnresult; } } 接下来我们来实现上面这一个抽象类,解释一下下面的代码,在下面的代码中,我们声明了两个集合类,allTypes和types,其中allTypes中包含了我们呢上面所声明的所有类,但是我们具体的类型实际上只有两种即Mutt和EgypianMau,所以我们真正需要new出来的宠物只是types中所包含的类型,以后我们通过调用getTypes()便可以得到types中所包含的所哟类型。
publicclassLiteralPetCreatorextendsPetCreator{ @SuppressWarnings("unchecked") publicstaticfinalList>allTypes=Collections.unmodifiableList( Arrays.asList(Pet.class,Dog.class,Cat.class,Mutt.class,EgyptianMau.class)); privatestaticfinalList >types=allTypes.subList( allTypes.indexOf(Mutt.class),allTypes.size()); publicList >getTypes(){ returntypes; } } 总体的逻辑已经完成了,最后我们实现用来统计集合中相关Pet类个数的TypeCounter类。解释一下isAssignalbeFrom()方法,它可以判断一个反射类是某个反射类的子类或者间接子类。而getSuperclass()顾名思义就是得到某个反射类的父类了。
publicclassTypeCounterextendsHashMap,Integer>{ privateClass>baseType; publicTypeCounter(Class>baseType){ this.baseType=baseType; } publicvoidcount(Objectobj){ Class>type=obj.getClass(); if(!baseType.isAssignableFrom(type)){ thrownewRuntimeException( obj+"incorrecttype"+type+",shouldbetypeorsubtypeof"+baseType); } countClass(type); } privatevoidcountClass(Class>type){ Integerquantity=get(type); put(type,quantity==null?1:quantity+1); Class>superClass=type.getSuperclass(); if(superClass!=null&&baseType.isAssignableFrom(superClass)){ countClass(superClass); } } @Override publicStringtoString(){ StringBuilderresult=newStringBuilder("{"); for(Map.Entry ,Integer>pair:entrySet()){ result.append(pair.getKey().getSimpleName()); result.append("="); result.append(pair.getValue()); result.append(","); } result.delete(result.length()-2,result.length()); result.append("}"); returnresult.toString(); } } 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。