0

I have a method like this in Java:

@Bean(name = "redisZSetTemplate")
    <T> RedisTemplate<String, T> redisTemplateZSet(RedisConnectionFactory redisConnectionFactory) {
}

what should I do to get the T type like T.getClass() or T.class? I have tried this way, add import com.fasterxml.jackson.core.type.TypeReference;, then get the T class like this:

Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(new TypeReference<T>(){}.getClass());

seems the new TypeReference<T>(){}.getClass() get current method class. not the type of T class。 I want to get the type because I want to parse the data type when fetched from redis. this is the full code looks like:

@Bean(name = "redisZSetTemplate")
    <T> RedisTemplate<String, T> redisTemplateZSet(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, T> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        template.setEnableTransactionSupport(true);
        Class<T> tClass = (Class<T>)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(tClass);
        ObjectMapper mapper = new ObjectMapper();
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        serializer.setObjectMapper(mapper);
        template.setValueSerializer(serializer);
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(serializer);
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        return template;
    }

store the ArticleZSet object into redis zset and deserilized it from redis. Actually I think define the T is more simplicity, what I am doing like this way:

@Bean(name = "redisObjectTemplate")
    RedisTemplate<String, Object> redisTemplateObject(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        template.setEnableTransactionSupport(true);
        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        serializer.setObjectMapper(mapper);
        template.setValueSerializer(serializer);
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(serializer);
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        return template;
    }

    /**
     * @return
     */
    @Bean(name = "redisIntegerTemplate")
    RedisTemplate<String, Integer> redisTemplateInt(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Integer> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        template.setEnableTransactionSupport(true);
        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        serializer.setObjectMapper(mapper);
        template.setValueSerializer(serializer);
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(serializer);
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        return template;
    }

    /**
     * @return
     */
    @Bean(name = "redisLongTemplate")
    RedisTemplate<String, Long> redisTemplateLong(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Long> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        template.setEnableTransactionSupport(true);
        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Long.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        serializer.setObjectMapper(mapper);
        template.setValueSerializer(serializer);
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(serializer);
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        return template;
    }

I have to define each template for different data type, that make the code ugly. More simple way, I want to just define 1 template and with the generic data type that could make the code more simplicity and reuseable.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
Dolphin
  • 29,069
  • 61
  • 260
  • 539
  • no, I have already read this question and it could not solve this problem. @kaya3 – Dolphin Oct 16 '22 at 07:17
  • Please expand your example to explain why you want to get the type. – tgdavies Oct 16 '22 at 07:19
  • I have added some content that explained why I want to get the T type, when I deserilized the date fetched from redis zset, I need to deserilized it and using in the next step. @tgdavies – Dolphin Oct 16 '22 at 07:25
  • 2
    Presumably when you call `redisTemplateZSet` you know what T is. Can you just pass the class as a parameter? – tgdavies Oct 16 '22 at 07:31
  • In fact ... tdavies's suggestion is the only generic approach that is going to work. And it is the solution that is suggested by https://stackoverflow.com/a/3437930/139985. So in fact, the dup link suggested by kaya3 is the correct link ... and ... you haven't read it properly. – Stephen C Oct 16 '22 at 07:35
  • While it might be difficult to apply the solution in your use-case, it is the only generic solution. You might be able to do something hacky with a custom deserializer class that tests the serialized data and chooses a class for `T` based on what it finds. – Stephen C Oct 16 '22 at 07:40
  • Or ... you could try to apply https://github.com/spring-projects/spring-data-redis/pull/2375 to a local build of Redis. – Stephen C Oct 16 '22 at 07:43

1 Answers1

1

Is it possible to get the type from generic type in java.

The answer to the general question is No, as explained in

and similar questions. Basically, you can't get the class of T at runtime. If you need to know the class at runtime, it needs to be either passed as a java.lang.Class parameter, or inferred from an actual instance of the T type. (And the latter can only be done reliably if T is a leaf class.)


There is no direct solution in the Redis use-case either, as of the time of writing.

However, I came across an unmerged Pull Request that claims to support this in the Spring-Data Redis binding; see pull request #2375. If you want to try it, you will need to figure out how to apply the change to ... whatever version of whatever Redis binding you are actually using. That will probably involve creating a (temporary) fork.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216