0

I am trying to generate the instance of typed class at run time to work with redis.

Here is my code, but its failing to create the instance of typed class T.

interface ObjectFactory<T>
{
    T create() throws Exception;
}

Here is the class that create the instance of T

import java.lang.reflect.ParameterizedType;

public class RedisObjectConstructor<T> implements ObjectFactory<T>
{

    @SuppressWarnings("unchecked")
    @Override
    public T create() throws Exception
    {
        try
        {
            return (T) ((Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();
        }
        catch (Exception e)
        {
            throw new Exception();
        }
    }
}

Here is logic to actually set and get value of domain field:

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.stereotype.Component;

@Component
public class RedisBasicEntity<T>
{
    private final ObjectFactory<T> factory;

    public RedisBasicEntity(ObjectFactory<T> factory)
    {
        this.factory = factory;
    }

    public Map<String, String> getFields(T redisDomain, Class<? extends Annotation> ann)
    {
        Map<String, String> redisHash = new HashMap<String, String>();
        Class<?> clazz = redisDomain.getClass();
        while (clazz != null)
        {
            for (Field field : clazz.getDeclaredFields())
            {
                if (field.isAnnotationPresent(ann))
                {
                    field.setAccessible(true);
                    try
                    {
                        Object value = field.get(redisDomain);
                        if (value != null)
                        {
                            redisHash.put(field.getName(), value.toString());
                            System.out.println(field.getName() + ":" + value.toString());
                        }
                    }
                    catch (IllegalArgumentException e)
                    {

                    }
                    catch (IllegalAccessException e)
                    {

                    }
                }
            }
            c = c.getSuperclass();
        }
        return redisHash;

    }

    public T getRedisEntity(Map<String, String> redisHash, Class<? extends Annotation> ann) throws Exception
    {
        T redisDomain = factory.create();
        Class<?> clazz = redisDomain.getClass();
        while (clazz != null)
        {
            for (Field field : clazz.getDeclaredFields())
            {
                if (field.isAnnotationPresent(ann))
                {
                    field.setAccessible(true);
                    try
                    {
                        FieldUtils.writeDeclaredField(field, field.getName(), redisHash.get(field.getName()));
                    }
                    catch (Exception e)
                    {

                    }
                }
            }
        }
        return redisDomain;
    }
}

Here is my test class:

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;

import com.ranker.annotations.rediskey.RedisHashKey;
import com.ranker.api.dao.iListQualityDAO;
import com.ranker.app.redis.domain.list.ListQuality;
import com.ranker.app.redis.repository.support.RedisBasicEntity;
import com.ranker.app.redis.repository.support.RedisObjectConstructor;

public class ListQualityDAO implements iListQualityDAO
{
    @Autowired 
    private RedisBasicEntity<ListQuality> redisBasicEntity;

    public static ListQuality saveListQuality()
    {
        RedisObjectConstructor<ListQuality> redisObjectConstructor = new RedisObjectConstructor<ListQuality>();
        RedisBasicEntity<ListQuality> redisBasicEntity = new RedisBasicEntity<ListQuality>(redisObjectConstructor);
        ListQuality listQuality = new ListQuality();
        listQuality.setBurialScore(10.0);
        Map<String, String> redisHash = redisBasicEntity.getFields(listQuality, RedisHashKey.class);
        return null;
    }

    public static ListQuality getListQuality() throws Exception
    {
        RedisObjectConstructor<ListQuality> redisObjectConstructor = new RedisObjectConstructor<ListQuality>();
        RedisBasicEntity<ListQuality> redisBasicEntity = new RedisBasicEntity<ListQuality>(redisObjectConstructor);
        Map<String, String> redisHash = new HashMap<String, String>();
        redisHash.put("satisfactionScore", "10.00");
        redisHash.put("burialScore", "5.00");
        redisHash.put("burialState", "true");
        ListQuality listQuality = redisBasicEntity.getRedisEntity(redisHash, RedisHashKey.class);
        return listQuality;

    }


    public static void main(String args[]) throws Exception
    {
        ListQualityDAO.getListQuality();
    }
}

My Annotation that used to identify the field:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisHashKey {
}

Please let me know what am doing wrong here:

Here is the exception that am getting when i do test run:

Exception in thread "main" java.lang.Exception at com.ranker.app.redis.repository.support.RedisObjectConstructor.create(RedisObjectConstructor.java:19) at com.ranker.app.redis.repository.support.RedisBasicEntity.getRedisEntity(RedisBasicEntity.java:61) at com.ranker.api.dao.impl.ListQualityDAO.getListQuality(ListQualityDAO.java:37) at com.ranker.api.dao.impl.ListQualityDAO.main(ListQualityDAO.java:45)

geek
  • 988
  • 10
  • 30
  • Why are you throwing out the original exception in `RedisObjectConstructor.create()`? Wouldn't the original exception, with its type, description, and stack trace be more useful in diagnosing any problems? – Dan Getz Jun 19 '15 at 05:57
  • This is just a test code, Actually what i want to know here is how can I create the instance of T at run time: try { return (T) ((Class) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).newInstance(); } catch (Exception e) { } – geek Jun 19 '15 at 05:59
  • Right, well, you got rid of the exception that might have told you why, threw a new one, and are posting a stack trace for the new one. If you don't have a good reason to be doing that, I suggest you don't. Just don't catch the exception in `RedisObjectConstructor.create()`, and you'll see what your error is. – Dan Getz Jun 19 '15 at 06:03
  • You should **almost never** catch an exception and then get rid of the exception you caught. But you're doing it almost every chance you get. – Dan Getz Jun 19 '15 at 06:05
  • I got your point: here is the actual exception:Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType at com.ranker.app.redis.repository.support.RedisObjectConstructor.create(RedisObjectConstructor.java:14) at com.ranker.app.redis.repository.support.RedisBasicEntity.getRedisEntity(RedisBasicEntity.java:59) at com.ranker.api.dao.impl.ListQualityDAO.getListQuality(ListQualityDAO.java:37) at com.ranker.api.dao.impl.ListQualityDAO.main(ListQualityDAO.java:45) – geek Jun 19 '15 at 06:09
  • Looking at your code, it looks like you might have already read the answer to your question, but missed part of it. [Is this what you read?](http://stackoverflow.com/a/75345/3004881) That answers your question: you can only do that trick **if you create a subclass.** They give an example of using an anonymous subclass to achieve what you're looking for. – Dan Getz Jun 19 '15 at 06:27
  • possible duplicate of [Create instance of generic type in Java?](http://stackoverflow.com/questions/75175/create-instance-of-generic-type-in-java) – Dan Getz Jun 19 '15 at 06:31

2 Answers2

1

Using TypeTools, your RedisObjectConstructor.create implementation could be:

Class<T> type = (Class<T>)TypeResolver.resolveRawArgument(ObjectFactory.class, getClass());
return type.newInstance();

Note: This only works if the class you put this in parameterizes T with an actual value. Ex:

class RedisStringConstructor extends RedisObjectConstructor<String> { //...

This way the type argument String is preserved in the bytecode and can be recovered. Simply instantiating RedisObjectConstructor with some type argument will not allow it to be recovered:

new RedisObjectConstructor<String>(); // can't recover String
Jonathan
  • 5,298
  • 33
  • 46
  • @TrapII It should work on 5 and up. The catch is the class you put this in needs to parameterize `T` with an actual value, like `String`. Upading post to reflect this. – Jonathan Jun 23 '15 at 18:55
  • May be I am not using the good version, but `Method.isDefault()` doesn't exists in Java7. This method is used in TypeTools. +1 for enlighting me on when the generic type is preserved. – TrapII Jun 24 '15 at 06:53
  • @TrapII I'd love to see an example of how you're hitting the `Method.isDefault` code in Java 7. That code path is obviously only intended for lambdas. Please file an issue if you can! https://github.com/jhalterman/typetools/issues – Jonathan Jun 27 '15 at 05:27
0

As specified in this stackoverflow response, I modified the create method of your RedisObjectConstructor<T> this way:

public class RedisObjectConstructor<T> implements ObjectFactory<T> {
    private final Class<T> type;

    public RedisObjectConstructor(Class<T> type) {
        this.type = type;
    }

    @Override
    public T create() throws Exception {
        return type.newInstance();
    }
}

And instanciate it like this:

RedisObjectConstructor<ListQuality> redisObjectConstructor = 
            new RedisObjectConstructor<ListQuality>(ListQuality.class);

And there is no more cast exception problem.

Community
  • 1
  • 1
TrapII
  • 1,867
  • 13
  • 15
  • See here we are explicitly passing the type, but what i need is at runtime it has to create the object based on the type. My Declaration SomeClass, and when i pass SomeClass It should return the ListQuality Object and If i do SomeClass I need to get the User object. Is this clear?? Main point here is i dont like to pass the Type here, I have to identify at run time based on the Type passed from ParameterizedType – geek Jun 19 '15 at 17:33
  • As explained in the SO response I cite, you cannot access the type: it is no more present at runtime. In the example you give, the type is known at compile time, so why not using it ? If you want to create dynamically your DAO objects, given their class, I would suggest to go for `Class.forName("xxx")` construction and save somewhere the class name of your object. – TrapII Jun 23 '15 at 06:55