Java Reflect Basics

I used to believe Reflect in Java was difficult because Core Java placed relevant chapters after Exceptions and Lambda. This is rather confusing. I am not ready for it until my senior in Vidar-Team, Ash , assigned a homework to figure out three ways to enable subclasses to get access private fields or methods of their superclass. All what I can think of is writing public getter/setter methods in superclass or making modifier private to protected. It was then Ash mentioned Reflect, which I almost forgot.

In fact, Reflect is used pretty common in Java, especially dealing with JDBC. Reflect is designed to fetch or revise details of a class when you have already compile it.

Every Reflect starts with a Class.forName();

When you’re writing Reflect, the first thing you do is to define which class you want to fetch. forName(String className) method returns a Class. Open your eye and watch it carefully. It is capitalized, meaning it’s a special type. So do Method, Field, and Constructor.

I’m tired of paraphrasing Reflect documentation, while copying and pasting Java documentation looks bored. Thus, I decide it to demonstrate the usage of **Reflect **through simple codes.

Student is a class with both public and private fields and several public methods.

package example.Reflect

public class Student {
    pubic String name;
    private int id;
    private int score; 

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public void getScore(){
        return score;
    }

    public void setScore(int score){
        this.score=score;
    }

    public void showAll(){
        System.out.println(this.name+"'s ID is "+this.id+".His score is"+this.score);
    }

    private int preID(int id){
        return (1819000+id);
    }


}

In order to test its Object-Oriented features, the main() method is written in another class)


public static void main( String[] args ){ try { Class c = Class.forName("example.Reflect.Student"); Field[] field = c.getFields(); for(Field f:field){ System.out.println(f); } System.out.println("##########") Methods[] method=c.getMethods(); for(Method m:method){ System.out.println(m); } } catch (ClassNotFoundException e) { e.printStackTrace(); //IDE will remind you if you forget to throw this exception } catch(NoSuchFieldException e){ e.printStackTrace(); }catch(NoSuchMethodException e){ e.printStackTrace(); } } /*result: public String example.Reflect.Student.name ########## public java.lang.String example.Reflect.Student.getScore() public int example.Reflect.Student.getID() public void example.Reflect.Student.setScore() public void exmaple.Reflect.Student.setID() public void example.Reflect.Student.showAll() public final void java.lang.Object.wait() throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public java.lang.String java.lang.Object.toString() public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll() */

As you can see, only public field is printed, and a whole bunch of methods printed belongs to java.lang.Object, the superclass of all other classes.

So, how to fetch both public and private methods and fields of Student, leaving those in Object aside?

Easy, change getFields() to getDeclaredFields, and similarly, getMethods() to getDeclaredMethods().

Rerun:

/*result:
private int example.Reflect.Student.id
private int example.Reflect.Student.score
public java.lang.String example.Reflect.name
private int example.Reflect.Student.preID(int id) <---
public java.lang.String example.Reflect.Student.getScore()
public int example.Reflect.Student.getID()
public void example.Reflect.Student.setScore()
public void exmaple.Reflect.Student.setID()
*/
How to Create a New Instance of a class using Reflect?

For those classes that only posses default constructor, newing an instance is fairly easy.

public static void main(String[] args){
    try{
        Class c=Class.forName("example.Reflect.Student");
        Student student=(Student)c.newInstance();
        //student's name uninitalized.
        student.setId(100);
        student.setScore(100);
        student.showAll();
    }catch(ClassNotFoundException e){
        e.printStackTrace();
    }
}//result: null's ID is 100.His score is 100

Only thing to mention is that newInstance() returns an Object, so you have to cast it to the particular one.

What if the target class has a constructor with parameters? Introducing Constructor.

Still taking class Student as example, after I rewrite the constructor into one with parameters, other parts remaining the same:

class Student{
    ....
    public Student(String name,int id, int score){
        this.name=name;
        this.id=id;
        this.score=score;
    }
    ...
}

In main() method:

public static void main(String[] args){
    try{
        Class c=Class.forName("example.Reflect.Student");
        //Student student=(Student)c.newInstance();<-- throw NoSuchMethodException(no default constructor!)

        Constructor constructor=c.getConstructor(String.class,int.class,int.class);
        Student student=(Student)constructor.newInstance("Michael",100,99);
        student.showAll();


    }catch(ClassNotFoundException e){
        //exception forName
        e.printStackTrace();
    }catch(NoSuchMethodException e){
        e.printStackTrace();
    }catch(IllegalAccessException e){
        //Some methods or constructors may be private or protected,or inaccessible
        e.printStackTrace();
    }
    catch(InvocationTargetException e){
        //Exceptions when newing with Constructor(with capital C)
        e.printStackTrace();
    }catch(InstantiationException e){
        e.printStackTrace();
    }


}//result:Michael's ID is 100.His score is 99

Do remember java.lang.reflect.* should be imported automatically by IDE or by you yourself, and don't forget to throw these exceptions.

Back to the Very Beginning: How can a Subclass Get Access to Private Fields or Methods of its Parent?

Ladies and gentleman, hereby I, with honor, introduce you setAccessible() .

To make it easy to understand, I'll rewrite class Student all over again, and then write another subclass of it.

package example.Reflect

public class Student {
    private String name;
    private int id;
    private int score; 

    public Student(String name,int id,int score){
        this.name=name;
        this.id=IDProcess(id);
        this.score=score;
    }

    private int IDProcess(int id){
        return (1801000+id);
    }

}

I devise of a Mechanic class that extends Student.

package example.Reflect

public class Mechanic extends Student{

    public Mechanic(String name,int id,int score){
        super(name,id,score);
    }

}

OK, here comes a problem. What if I want to add a method in Mechanic instead of Student, printing out all the information of a Mechanic instance without writing any getter/setter in Student, and no changes to the modifier private ?

1.Fetching Fields

I don't know haven't you tried, but it is really frustrating that neither this.name nor super.name works in this case. Without any public setter/getter, the work seems impossible. Of coarse you can copy and paste all the fields of Student to Mechanic, but that would be stupid.

It is time for to invite our protagonist today out. Reflect!Showing the code probably gives you a better understanding.

public void showName() throws  ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException{
    Class c=Class.forName("example.Reflect.Student");
    Field field=c.getDeclaredField("name");
    //Coming!
    field.setAccessible(true);
    String name=(String)field.get(this);//must cast or it returns an Object
    System.out.println(name);
}

setAccessible()has one boolean parameter. It is set false as default until the programmer set it true.

Test it in main()

public static void main(String[] args){
   Mechanic mstudent=new Mechanic("Michael",100,100);
   try {
            mstudent.showName();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}//result: Michael

As seen in showName(), field.get()method is used to retrieve the value of the field of a specified instance. Similarly, field.set() method is used to set the value of the field of the instance.

For example:

public void setName(String name) throws ...{
    ...//Class.forName().. etc,just like showName()
    field.setAccessible(true);
    field.set(this,name);
}//..works exactly the same like writing setter in parent class.

It works all the same with other fields like ID and score. Due to limited space, I'll skip them.

2.Calling Methods

Almost resemble the codes above:

public void showID(int id) throws  ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException{
    Class c=Class.forName("example.Reflect.Student");
    Methods method=c.getDeclaredMethod("IDProcess",int.class);
    //inside the parentheses:methodName and its parameters(param can be one or more)
    method.setAccessible(true);
    System.out.println((int)method.invoke(this,id));
    //Don't forget to cast, or it'll return something like example.Reflect.Mechanics@10b28a4
}

Test it in main():

public static void main(String[] args){
   Mechanic mstudent=new Mechanic("Michael",100,100);
   try {
            mstudent.showID(100);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }catch(InvocationTargetException e){
            e.printStackTrace();
        }
    }
}//result: 1801100

As always, import java.lang.reflect.*. You could either wrap your thing in try/catch, or throw exceptions.

At the End of the Day

Above are just the very very basic usage of Java's Reflect. Michael is still learning and coding, on the arduous journey to become those who he admires and his mentors. Should any mistakes, even trivial typos, exists, please write your comment below to let Michael know. He'll be humbly taking your precious advise.

Why is ArrayList dynamic?

Anyone who has learned ArrayList systematically should have basic knowledge that ArrayList is a List based on dynamic array. You don’t have to worry about errors like ‘ArrayOutOfBoundary’, because the length, or size, is not static,unless you give it a boundary,or capacity when you initialize an ArrayList. How do Java achieve this function? Talk is cheap, read the code.


Before that, I believe it’s fairly essential to distinguish the differences between Capacity and size.
Capacity in numbers return the length of the Array, putting the null elements into consideration;
Size returns how many non-null elements the array contains.
For example:

int[] a = new int[10];
for(int i=0;i<5;i++){
    a[i]=i;
}
/*
The size of a is 5,while the capacity is 10,which never changes once you new it.
*/

Reading Comprehension Time!

//The default_capacity of ArrayList
private static final int DEFAULT_CAPACITY = 10;

//Empty Array Elements
transient Object[] elementData;

//default initial capacity
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

private int size;

//When using the default constructor
public ArrayList() {
    this.elementData =DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

//size=10 if you just new the ArrayList using default constructor; 
public boolean add(E e) {
    ensureCapacityInternal(size +1);// modCount incremented
    elementData[size++] = e;
    return true;
}

/*initialize the ArrayList size to 10;
 * Increases the capacity to ensure that it can hold at least the
 * number of elements specified by the minimum capacity argument.
 *
 * @param minCapacity the desired minimum capacity
 */

private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

/*Why spend so much on pursing minimum capacity at the cost of
 *making the code so complicated?
 *To save the precious memory, I guess.
 */


//When the programmer use the constructor with parameters that limits the size;
private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity)
            /*if the size is smaller than 10, it's no big deal if you create a 10 one,
            *since you can trim it;
            *but this is not the case when it's the opposite.
            */
             }
        return minCapacity;
    }

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;//modCount is to record how many times the capacity has changed;
        if (minCapacity - elementData.length > 0)
            /*if the size of Array at present is larger than the capacity of Array;
             *it works well even when minCapacity is set negative
             */
            grow(minCapacity);//literally, grow:)
    }


// There's a upper limit of the size of ArrayList
//Why MAX_VALUE-8? Because an array need a 8 bytes(size of an int)of memory 
//to store the size of itself
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;


private void grow(int minCapacity) {
    int oldCapacity = elementData.length;//oldCapacity is size
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    //The new capacity is the 1.5 times larger than the old one
    if (newCapacity - minCapacity < 0)
        //make sure capacity is the smallest
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        //Of course, if the array is too big...
        newCapacity = hugeCapacity(minCapacity);

    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
    //create an object array(a new one!) with all value from
    //elementData and a capacity of newCapacity 

    }

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) 
        // Capacity can't be a negative number,anyway
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        //if capacity is too big
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

Both remove(int index) and remove(Object o) calls fastRemove(int index):

 private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

As you can see, capacity of array does not change when elements are removed. This is because if you clear the unused memory immediately, then you need to take extra time to allocate them when you eventually decide to use them, which is low-efficient.

Conclusion:

Concrete and detailed analysis is in annotation of the code above.

Three core private methods of ArrayList, ensureCapacityInternal(), ensureExplicitCapacity(), and grow() (all parameters ignored) explain why it is dynamic. Capacity in the very beginning is 0 after newed by defualt constructor until add() is called. When the programmer calls either add(int index, Object o) or add(Object o), and it happens that the size of the list is larger than the present capacity, ensureCapacityInternal() will be called. It first decides how much memory to allocate , then JVM calls ensureExplicitCapacity(), and finally grow() method is called. Method grow() directly expands the capacity by 1.5 times and in the end System.copyOf() copy everything of the array with small capacity to the new 1.5 larger one, and returns the new array to programmers. Since these core methods are private and encapsulated, ArrayList shows up in a dynamic appearance.