user592433
user592433

Reputation: 131

How To Instantiate a java.util.ArrayList with Generic Class Using Reflection

How To Instantiate a java.util.ArrayList with Generic Class Using Reflection? I am writing a method that sets java.util.List on target object. A target object and a generic type of list is knowing in runtime:

public static void initializeList(Object targetObject, PropertyDescriptor prop, String gtype) {
    try {
        Class clazz = Class.forName("java.util.ArrayList<"+gtype+">");
        Object newInstance = clazz.newInstance();
        prop.getWriteMethod().invoke(targetObject, newInstance);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Upvotes: 13

Views: 27731

Answers (7)

Plan Action
Plan Action

Reputation: 1

This can init a Object contains List successfully.

package com.y24.init;

import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.SneakyThrows;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

public class ObjInit {
    @SneakyThrows
    public static void main(String[] args) {
        Object o = objInit(Persons.class);
    }

    @SneakyThrows
    public static Object objInit(Class<?> clazz) {
        //基础数据类型
        if (clazz == int.class || clazz == Integer.class) {
            Object defaultValue = 1;
            return defaultValue;
        } else if (clazz == long.class || clazz == Long.class) {
            Object defaultValue = 1L;
            return defaultValue;
        } else if (clazz == double.class || clazz == Double.class) {
            Object defaultValue = 3.14;
            return defaultValue;
        } else if (clazz == boolean.class || clazz == Boolean.class) {
            Object defaultValue = false;
            return defaultValue;
        } else if (clazz == String.class) {
            Object defaultValue = "Hello world!";
            return defaultValue;
        }

        Object obj = null;
        try {
            obj = clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 首先得到pojo所定义的字段
        Field[] fields = clazz.getDeclaredFields();
        for (Field curField : fields) {
            // 设置字段可访问(必须,否则报错)
            curField.setAccessible(true);

            Class<?> curFieldType = curField.getType();
            // 集合List元素
            if (curFieldType.equals(List.class)) {
                // 当前集合的泛型类型
                Type genericType = curField.getGenericType();
                if (genericType instanceof ParameterizedType) {
                    ParameterizedType pt = (ParameterizedType) genericType;
                    // 得到泛型里的class类型对象
                    Class<?> actualTypeArgument = (Class<?>) pt.getActualTypeArguments()[0];
                    List<Object> curEleList = new ArrayList<>();
                    //Object actualType = actualTypeArgument.newInstance();
                    //....actualType字段处理
                    Object actualType = objInit(actualTypeArgument);
                    curEleList.add(actualType);
                    curField.set(obj, curEleList);
                }
            } else {
                curField.set(obj, objInit(curFieldType));
            }
        }
        System.out.println(JSONObject.toJSONString(obj));
        return obj;
    }
}

@Data
class Persons {
    private int count;
    private List<Person> persons;
}

@Data
class Person {
    private String name;
    private int age;
    private Address address;
    private List<String> names;
}

@Data
class Address {
    private String city;
    private String zipCode;

    // getters and setters...
}

Upvotes: 0

alpian
alpian

Reputation: 4748

There's no need to do any reflection for creating your List. Just pass in some additional type information (usually done by passing a class of the correct type).

public static <T> List<T> createListOfType(Class<T> type) {
    return new ArrayList<T>();
}

Now you have a list of the required type you can presumably/hopefully set it directly on your targetObject without any reflection.

Upvotes: 7

Peter Lawrey
Peter Lawrey

Reputation: 533700

Generics is largely a compile time feature. What you are trying to do is the same as

public static void initializeList(Object targetObject, PropertyDescriptor prop, String gtype) {
    prop.getWriteMethod().invoke(targetObject, new ArrayList());
}

Note: This could change with Types in Java 7.

Upvotes: 0

ram singh
ram singh

Reputation: 1

An object doesn't know about its generic type at execution time. Just create a new instance of the raw type (java.util.ArrayList). The target object won't know the difference (because there isn't any difference).

Upvotes: 0

biziclop
biziclop

Reputation: 49794

Generics only work at compile time, therefore if you're using reflection, they won't be available.

See more about this here.

Upvotes: 0

Andrzej Doyle
Andrzej Doyle

Reputation: 103827

Generics are a compile-time only "trick".

Reflection is runtime-only.

Basically, you can't - you can only create a "raw" ArrayList. If you need to pass it into methods that take generic parameters, casting it directly after construction will be safe (regardless of the "unchecked" warning). In this example, there's no compile-time type safety anyway due to using general Objects, so no casting is needed.

Upvotes: 2

Jon Skeet
Jon Skeet

Reputation: 1502406

An object doesn't know about its generic type at execution time. Just create a new instance of the raw type (java.util.ArrayList). The target object won't know the difference (because there isn't any difference).

Basically Java generics is a compile-time trick, with metadata in the compiled classes but just casting at execution time. See the Java generics FAQ for more information.

Upvotes: 15

Related Questions