Java reflection mechanism instance code sharing

  • 2020-11-26 18:47:23
  • OfStack

The purpose of this article is to provide a comprehensive introduction to Java reflection. Hopefully, you will have a comprehensive understanding of Java reflection.

Before reading this article, you can refer to reunderstanding Java generics.

preface

The Java reflection mechanism is a very powerful feature that can be seen in many large projects such as Spring. The reflection mechanism enables us to obtain the type information of objects at run time, which enables us to implement design patterns such as factory patterns and proxy patterns, as well as solve vexing problems such as Java generic erasure. In this paper, we apply the reflection mechanism of Java 1 from the perspective of practical application.

Reflection basis

p.s: This article requires a certain level of understanding of API as a reflection mechanism. If you have not been exposed to Quick before, it is recommended to read Quick Start in the official documentation.

Before we apply the reflection mechanism, let's first look at how to get the reflection class corresponding to 1 object Class In Java we have three ways to get a reflection class for an object.

Through the getClass method

In Java, one for each Object There is one getClass Method, we can get the corresponding reflection class of this object through getClass method:


String s = "ziwenxie";
Class<?> c = s.getClass();

We can also call Class Static methods of a class forName :


Class<?> c = Class.forName("java.lang.String");

Using class.

Or we could just use it .class :


Class<?> c = String.class;

Get type information

We mentioned at the beginning of this article that one of the great benefits of reflection is that it allows us to get the type information of an object at run time. Let's look at this in detail with an example.

First of all, we are typeinfo.interfacea Create a new interface below the package A :


package typeinfo.interfacea;
public interface A { void f(); }

And then we went to typeinfo.packageaccess Create a new interface below the package C , the interface C Inherits from interface A , and we have also created several other methods for testing. Note that the permissions for the following methods are different.


package typeinfo.packageaccess;
import typeinfo.interfacea.A;
class C implements A {
  public void f() { System.out.println("public C.f()"); }
  public void g() { System.out.println("public C.g()"); }
  protected void v () { System.out.println("protected C.v()"); }
  void u() { System.out.println("package C.u()"); }
  private void w() { System.out.println("private C.w()"); }
}
public class HiddenC {
  public static A makeA() { return new C(); }
}

in Object2 In the method we used several new API, among them Object3 A method used to get the Class class to refer to the object based on the method name, which we then call Object4 Methods that pass into the actual object can trigger the object's related methods:


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();
    a.f();
    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);
  }
}

We can tell from the output, no matter what Object5 . Object6 . Object7 or Object8 Methods, which we can all call freely through the reflection class. Of course, just to show the power of reflection, this technique is not recommended in practice.


public C.f()
typeinfo.packageaccess.C
public C.g()
package C.u()
protected C.v()
private C.w()

The application practice

We have the following business scenario where we have a generic collection class Object9 , we need to count each specific in this collection class getClass0 How many are there? Due to the generic erasure of Java, note the similarity getClass1 This is definitely not the case, because JVM will treat all objects in the collection at run time after the compiler has done static type checking getClass0 But you don't know getClass0 What does it represent getClass4 or getClass5 , so by run-time the object's type information is all but lost. p. s: About generic erasers: I explained it in detail in my last article.

To implement our example above, let's first define a few 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 Dog() { super(); }
}
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(); }
}

The above getClass0 Class inherits from getClass7 . getClass7 The implementation of the class is a little bit more complicated 1, so we implemented getClass9 The interface, which redefines the rules for comparing classes, doesn't matter if it's not clear, we've abstracted it out, so it doesn't matter if we don't understand the implementation.


public class Individual implements 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 secendCompare = name.compareTo(arg.name);
      if (secendCompare != 0) {
        return secendCompare;
      }
    }
    return (arg.id < id ? -1 : (arg.id == id ? 0 : 1));
  }
}

Here we create an abstract class Class0 In the future, we're going to call Class1 The correlation can be obtained directly by the method getClass0 A collection of classes. This USES something we didn't mention above newInstance() Method, which returns an instance of the class that the Class class really refers to. What does that mean? For example, the declaration new Dog().getClass().newInstance() And direct new Dog() It's 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;
  }
}

Let's implement the above abstract class and interpret the code below 1. In the code below, we declare two collection classes, allTypes and types , including allTypes Contains all of the classes we declared above, but there are really only two of our specific types Mutt and EgypianMau So we really need new The pet that comes out is just types Contains the types that we will call later getTypes() You can get types All yo types contained in.


Class<?> c = Class.forName("java.lang.String");
0

The overall logic is complete, and finally we implement it to count the correlation in the set getClass0 The number of classes TypeCounter Class. Explanation 1 isAssignalbeFrom() Method that determines whether a reflection class is a subclass or indirect subclass of a reflection class. while getSuperclass() As the name implies, you get the parent of a reflection class.


Class<?> c = Class.forName("java.lang.String");
1

conclusion

That's the end of this article on Java reflection mechanism example code sharing, I hope to help you. Those who are interested can continue to see this site:

Java programming printing shopping receipt implementation code

Implementation of references and dynamic proxies in Java in detail

Java programming simple eclipse code sharing

If there is any deficiency, please let me know. Thank you for your support!


Related articles: