Java Reflection for Arrays

1. Overview

Arrays have a component type and a length (which is not part of the type). Arrays may be manipulated either in their entirety or component by component. Reflection provides the java.lang.reflect.Array class.

The source code for this post is available on GitHub.

The below class diagram shows the Array class provided all the reflection APIs.
Java Reflection for Arrays

2. Java Reflection for Arrays

  • Identifying Array Types - describes how to determine if a class member is a field of array type
  • Creating New Arrays - illustrates how to create new instances of arrays with simple and complex component types
  • Getting and Setting Arrays and Their Components - shows how to access fields of type array and individually access array elements

2.1 Identifying Array Types

Array types may be identified by invoking Class.isArray().
The ArrayFind example identifies the fields in the named class that are of array type and reports the component type for each of them.
package com.javaguides.reflection.arrays;

import static java.lang.System.out;

import java.lang.reflect.Field;
 
public class ArrayFind {
    public static void main(String... args) {
    boolean found = false;
    try {
        Class<?> cls = Class.forName("java.nio.ByteBuffer");
        Field[] flds = cls.getDeclaredFields();
        for (Field f : flds) {
        Class<?> c = f.getType();
        if (c.isArray()) {
            found = true;
            out.format("%s%n"
                               + "           Field: %s%n"
                   + "            Type: %s%n"
                   + "  Component Type: %s%n",
                   f, f.getName(), c, c.getComponentType());
        }
        }
        if (!found) {
        out.format("No array fields%n");
        }
 
        // production code should handle this exception more gracefully
    } catch (ClassNotFoundException x) {
        x.printStackTrace();
    }
    }
}
Output:
final byte[] java.nio.ByteBuffer.hb
           Field: hb
            Type: class [B
  Component Type: byte
Let's pass java.lang.Throwable argument to Class.forName("java.lang.Throwable") method. An array of reference type StackTraceElement Output:
private static final java.lang.StackTraceElement[] java.lang.Throwable.UNASSIGNED_STACK
           Field: UNASSIGNED_STACK
            Type: class [Ljava.lang.StackTraceElement;
  Component Type: class java.lang.StackTraceElement
private java.lang.StackTraceElement[] java.lang.Throwable.stackTrace
           Field: stackTrace
            Type: class [Ljava.lang.StackTraceElement;
  Component Type: class java.lang.StackTraceElement
private static final java.lang.Throwable[] java.lang.Throwable.EMPTY_THROWABLE_ARRAY
           Field: EMPTY_THROWABLE_ARRAY
            Type: class [Ljava.lang.Throwable;
  Component Type: class java.lang.Throwable

2.2 Creating New Arrays

Reflection supports the ability to dynamically create arrays of arbitrary type and dimensions via java.lang.reflect.Array.newInstance().
Let's write an example to create an array via Reflection.
package com.javaguides.reflection.arrays;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.Arrays;
import static java.lang.System.out;
 
public class ArrayCreator {
    private static String s = "java.math.BigInteger bi[] = { 123, 234, 345 }";
    private static Pattern p = Pattern.compile("^\\s*(\\S+)\\s*\\w+\\[\\].*\\{\\s*([^}]+)\\s*\\}");
 
    public static void main(String... args) {
        Matcher m = p.matcher(s);
 
        if (m.find()) {
            String cName = m.group(1);
            String[] cVals = m.group(2).split("[\\s,]+");
            int n = cVals.length;
 
            try {
                Class<?> c = Class.forName(cName);
                Object o = Array.newInstance(c, n);
                for (int i = 0; i < n; i++) {
                    String v = cVals[i];
                    Constructor ctor = c.getConstructor(String.class);
                    Object val = ctor.newInstance(v);
                    Array.set(o, i, val);
                }
 
                Object[] oo = (Object[])o;
                out.format("%s[] = %s%n", cName, Arrays.toString(oo));
 
            // production code should handle these exceptions more gracefully
            } catch (ClassNotFoundException x) {
                x.printStackTrace();
            } catch (NoSuchMethodException x) {
                x.printStackTrace();
            } catch (IllegalAccessException x) {
                x.printStackTrace();
            } catch (InstantiationException x) {
                x.printStackTrace();
            } catch (InvocationTargetException x) {
                x.printStackTrace();
            }
        }
    }
}
Output:
java.math.BigInteger[] = [123, 234, 345]

2.3 Getting and Setting Arrays and Their Components

  1. An array field may be set or retrieved in its entirety or component by component.
  2. To set the entire array at once, use java.lang.reflect.Field.set(Object obj, Object value).
  3. To retrieve the entire array, use Field.get(Object).
  4. Individual components can be set or retrieved using methods in java.lang.reflect.Array.

Setting a Field of Type Array

The GrowBufferedReader example illustrates how to replace the value of a field of type array. In this case, the code replaces the backing array for a java.io.BufferedReader with a larger one. (This assumes that the creation of the original BufferedReader is in code that is not modifiable; otherwise, it would be trivial to simply use the alternate constructor BufferedReader(java.io.Reader in, int size) which accepts an input buffer size.)
package com.javaguides.reflection.arrays;

import java.io.BufferedReader;
import java.io.CharArrayReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Arrays;
import static java.lang.System.out;
 
public class GrowBufferedReader {
    private static final int srcBufSize = 10 * 1024;
    private static char[] src = new char[srcBufSize];
    static {
    src[srcBufSize - 1] = 'x';
    }
    private static CharArrayReader car = new CharArrayReader(src);
 
    public static void main(String... args) {
    try {
        BufferedReader br = new BufferedReader(car);
 
        Class<?> c = br.getClass();
        Field f = c.getDeclaredField("cb");
 
        // cb is a private field
        f.setAccessible(true);
        char[] cbVal = char[].class.cast(f.get(br));
 
        char[] newVal = Arrays.copyOf(cbVal, cbVal.length * 2);
        if ("grow".length() > 0 && "grow".equals("grow"))
        f.set(br, newVal);
 
        for (int i = 0; i < srcBufSize; i++)
        br.read();
 
        // see if the new backing array is being used
        if (newVal[srcBufSize - 1] == src[srcBufSize - 1])
        out.format("Using new backing array, size=%d%n", newVal.length);
        else
        out.format("Using original backing array, size=%d%n", cbVal.length);
 
        // production code should handle these exceptions more gracefully
    } catch (FileNotFoundException x) {
        x.printStackTrace();
    } catch (NoSuchFieldException x) {
        x.printStackTrace();
    } catch (IllegalAccessException x) {
        x.printStackTrace();
    } catch (IOException x) {
        x.printStackTrace();
    }
    }
}
Output:
Using new backing array, size=16384

Accessing Elements of a Multidimensional Array

Multi-dimensional arrays are simply nested arrays. A two-dimensional array is an array of arrays. A three-dimensional array is an array of two-dimensional arrays, and so on.
The CreateMatrix example illustrates how to create and initialize a multi-dimensional array using reflection.
package com.javaguides.reflection.arrays;

import java.lang.reflect.Array;
import static java.lang.System.out;
 
public class CreateMatrix {
    public static void main(String... args) {
        Object matrix = Array.newInstance(int.class, 2, 2);
        Object row0 = Array.get(matrix, 0);
        Object row1 = Array.get(matrix, 1);
 
        Array.setInt(row0, 0, 1);
        Array.setInt(row0, 1, 2);
        Array.setInt(row1, 0, 3);
        Array.setInt(row1, 1, 4);
 
        for (int i = 0; i < 2; i++)
            for (int j = 0; j < 2; j++)
                out.format("matrix[%d][%d] = %d%n", i, j, ((int[][])matrix)[i][j]);
    }
}
Output:
matrix[0][0] = 1
matrix[0][1] = 2
matrix[1][0] = 3
matrix[1][1] = 4

3. Reference

Comments