This article aims to give a comprehensive introduction to the Java reflection mechanism. I hope that through this article, you will have a comprehensive understanding of the relevant content of Java reflection.
Before reading this article, you can refer to " Re-understanding Java Generics " .
Preface
The Java reflection mechanism is a very powerful function. Reflections can be seen in many large-scale projects such as Spring and Mybatis. Through the reflection mechanism, we can obtain object type information during operation. Using this feature, we can implement design patterns such as factory mode and proxy mode, and can also solve distressing problems such as Java generic erasure. In this article, we will apply the Java reflection mechanism from the perspective of practical applications.
Reflection basis
ps: This article requires readers to have a certain degree of understanding of the reflection mechanism API. If you have not been exposed to it before, it is recommended to look at the Quick Start of the official document first.
Before applying the reflection mechanism, let’s first look at how to get the reflection Class corresponding to an object. In Java, we have three ways to get the reflection class of an object.
By getClass method
In Java, each Object has a getClass method. Through the getClass method, we can get the corresponding reflection class of this object:
String s = "ziwenxie";Class<?> c = s.getClass();
We can also call the static method forName of Class class:
Class<?> c = Class.forName("java.lang.String"); Or we can use .class directly:
Class<?> c = String.class;
At the beginning of the article, we mentioned that one of the major benefits of reflection is that it allows us to obtain object type information during operation. Let’s take a look at it in detail with an example.
First, we create a new interface A under typeinfo.interfacea package:
package typeinfo.interfacea;public interface A { void f(); } Then we create a new interface C under the typeinfo.packageaccess package. Interface C inherits from interface A , and we also created several other methods for testing. Note that the permissions of the following methods are different.
package typeinfo.packageaccess;import typeinfo.interfacea.A;class C implements A { public void f() { System.out.println("public Cf()"); } public void g() { System.out.println("public Cg()"); } protected void v () { System.out.println("protected Cv()"); } void u() { System.out.println("package Cu()"); } private void w() { System.out.println("private Cw()"); }}public class HiddenC { public static A makeA() { return new C(); }} In callHiddenMethod() method, we use several new APIs, where getDeclaredMethod() is used to obtain a method that the Class class refers to the object according to the method name, and then we can trigger the object's related methods by calling invoke() method:
package typeinfo;import typeinfo.interfacea.A;import typeinfo.packageaccess.HiddenC;import java.lang.reflect.Method;public class HiddenImplementation { public static void main(String[] args) throws Exception { A a = HiddenC.makeA(); af(); System.out.println(a.getClass().getName()); // Oops! Reflection still allows us to call g(): callHiddenMethod(a, "g"); // And even methods that are less accessible! callHiddenMethod(a, "u"); callHiddenMethod(a, "v"); callHiddenMethod(a, "w"); } static void callHiddenMethod(Object a, String methodName) throws Exception { Method g = a.getClass().getDeclaredMethod(methodName); g.setAccessible(true); g.invoke(a); }} From the output results, we can see that whether it is public , default , protect or pricate method, we can call it freely through the reflection class. Of course, we are just to show the powerful power of reflection, and this technique is not recommended in actual development.
public Cf()typeinfo.packageaccess.Cpublic Cg()package Cu()protected Cv()private Cw()private Cw()
We have the following business scenario. We have a generic collection class List<Class<? extends Pet>> . We need to count how many specific Pet are in this collection class. Due to Java generic erasure, it is definitely not possible to pay attention to the practice similar to List<? extends Pet> , because after the compiler does static type check, the JVM will treat all the objects in the collection as Pet during the run, but it will not know whether Pet represents Cat or Dog , so the type information of the object is actually lost during the run. ps: About generic erasure: I have a detailed explanation in the previous article. Interested friends can take a look.
To implement our example above, we first define several classes:
public class Pet extends Individual { public Pet(String name) { super(name); } public Pet() { super(); }}public class Cat extends Pet { public Cat(String name) { super(name); } public Cat() { super(); }}public class Dog extends Pet { public Dog(String name) { super(name); }}public class EgyptianMau extends Cat { public EgyptianMau(String name) { super(name); } public EgyptianMau() { super(); }}public class Mutt extends Dog { public Mutt(String name) { super(name); } public Mutt() { super(); }} Pet class above inherits from Individual . The implementation of Individual class is a little more complicated. We implemented Comparable interface and redefined the class comparison rules. If we don’t understand it very well, it doesn’t matter. We have abstracted it, so it doesn’t matter if we don’t understand the implementation principle.
public class Individual implementations Comparable<Individual> { private static long counter = 0; private final long id = counter++; private String name; // name is optional public Individual(String name) { this.name = name; } public Individual() {} public String toString() { return getClass().getSimpleName() + (name == null ? "" : " " + name); } public long id() { return id; } public boolean equals(Object o) { return o instanceof Individual && id == ((Individual)o).id; } public int hashCode() { int result = 17; if (name != null) { result = 37 * result + name.hashCode(); } result = 37 * result + (int) id; return result; } public int compareTo(Individual arg) { // Compare by class name first: String first = getClass().getSimpleName(); String argFirst = arg.getClass().getSimpleName(); int firstCompare = first.compareTo(argFirst); if (firstCompare != 0) { return firstCompare; } if (name != null && arg.name != null) { int secondaryCompare = name.compareTo(arg.name); if (secendCompare != 0) { return secondaryCompare; } } return (arg.id < id ? -1 : (arg.id == id ? 0 : 1)); }} Below is a abstract class PetCreator . In the future, we can directly obtain the collection of related Pet classes by calling arrayList() method. Here we use the newInstance() method that we did not mention above. It will return an instance of the class that the Class class really refers to. What does this mean? For example, declaring new Dog().getClass().newInstance() and direct new Dog() are equivalent.
public abstract class PetCreator { private Random rand = new Random(47); // The List of the different getTypes of Pet to create: public abstract List<Class<? extends Pet>> getTypes(); public Pet randomPet() { // Create one random Pet int n = rand.nextInt(getTypes().size()); try { return getTypes().get(n).newInstance(); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } public Pet[] createArray(int size) { Pet[] result = new Pet[size]; for (int i = 0; i < size; i++) { result[i] = randomPet(); } return result; } public ArrayList<Pet> arrayList(int size) { ArrayList<Pet> result = new ArrayList<Pet>(); Collections.addAll(result, createArray(size)); return result; }} Next, let's implement the above abstract class and explain the following code. In the following code, we declare two collection classes, allTypes and types , among which allTypes contains all the classes declared above, but our specific types are actually only two types, namely Mutt and EgypianMau , so the pet we really need to get new is just the types contained in types . In the future, we can get the types contained in types by calling getTypes() .
public class LiteralPetCreator extends PetCreator { @SuppressWarnings("unchecked") public static final List<Class<? extends Pet>> allTypes = Collections.unmodifiableList( Arrays.asList(Pet.class, Dog.class, Cat.class, Mutt.class, EgyptianMau.class)); private static final List<Class<? extends Pet>> types = allTypes.subList( allTypes.indexOf(Mutt.class), allTypes.size()); public List<Class<? extends Pet>> getTypes() { return types; }} The overall logic has been completed, and finally we implement TypeCounter class used to count the number of relevant Pet classes in the set. Explain isAssignalbeFrom() method, which can determine that a reflection class is a subclass or indirect subclass of a reflection class. As the name suggests, getSuperclass() is to get the parent class of a reflection class.
public class TypeCounter extends HashMap<Class<?>, Integer> { private Class<?> baseType; public TypeCounter(Class<?> baseType) { this.baseType = baseType; } public void count(Object obj) { Class<?> type = obj.getClass(); if (!baseType.isAssignableFrom(type)) { throw new RuntimeException( obj + " incorrect type " + type + ", should be type or subtype of " + baseType); } countClass(type); } private void countClass(Class<?> type) { Integer quantity = get(type); put(type, quantity == null ? 1 : quantity + 1); Class<?> superClass = type.getSuperclass(); if (superClass != null && baseType.isAssignableFrom(superClass)) { countClass(superClass); } } @Override public String toString() { StringBuilder result = new StringBuilder("{"); for (Map.Entry<Class<?>, 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("} "); return result.toString(); }}Summarize
The above is all the content of this article about the example code sharing of Java reflection mechanism, and I hope it will be helpful to everyone. Interested friends can continue to refer to this site:
Java programming and printing shopping receipt implementation code
Detailed explanation of the implementation of references and dynamic proxy in Java
Java programming to implement simple code sharing of lunar eclipse
If there are any shortcomings, please leave a message to point it out. Thank you friends for your support for this site!