2

I have an abstract class that takes a generic, in this class there's some methods.

My problem is that I can't use the generic E inside methods.

public abstract class AbstractRepository<E> implements Repository<E> {

    final Class<E> typeParameterClass;

    public AbstractRepository(Class<E> typeParameterClass) {
        this.typeParameterClass = typeParameterClass;
    }

    ...

    @Override
    public E findById(Integer id) {
        try {
            this.entityManager.find(typeParameterClass, id);

        } catch (IllegalArgumentException error) {
            error.printStackTrace();

        } catch (PersistenceException error) {
            error.printStackTrace();
        }
        return null;
    }

I try to use generic E as parameter of 'find()' as below, but I got a error that says E is not defined.

@Override
public E findById(Integer id) {
    try {
        this.entityManager.find(E, id);

    } catch (IllegalArgumentException error) {
        error.printStackTrace();

    } catch (PersistenceException error) {
        error.printStackTrace();
    }
    return null;
}

To solve this I use typeParameterClass.

typeParameterClass is an argument that every other class that extends AbstractRepository have to pass, this argument is the class itself, but it does not seem right, since I already have the class in generic.

Example of the way how it is now:

@Repository
public class UserRepository extends AbstractRepository<User> {

    public UserRepository(){
        super(User.class);
    }

    ...
}

So, is there a way to use generic E inside methods?

Repository is an interface that also takes the generic E.

I'm using Spring with Hibernate.

FelipeP
  • 69
  • 8

2 Answers2

0

Generic type information does not exist in Java at run-time, so there is no way to determine the type parameter used for your concrete instance.

So the only way to handle this is via Class parameters, as you do, as these are designed to carry type information at run-time, and have a self-referencing generic parameter, which is why you can ensure the used Class type is of your type E by declaring it as Class<E>. Again, all generic type parameters are only compiler sugar and removed at run-time (so they only ensure nobody writes code passing a non-E Class in your instance).

Florian Albrecht
  • 2,266
  • 1
  • 20
  • 25
  • Not fully correct. You can get type parameters of parametrized generic class. It's a dirty hack, but it works. http://stackoverflow.com/questions/18707582/get-actual-type-of-generic-type-argument-on-abstract-superclass and http://stackoverflow.com/questions/9934774/getting-generic-parameter-from-supertype-class – Denis Kokorin Oct 20 '16 at 11:28
  • @Denis Yes... No. Just no. And it won't help here. – Florian Albrecht Oct 20 '16 at 11:33
  • @FlorianAlbrecht, why would it be of no help here? Those links show how to solve OP's problem, while you are saying that it is impossible. – user3707125 Oct 20 '16 at 11:36
  • 1
    Guys, none of these solutions is simpler than the one the OP already has implemented. And none is more stable and more "correct" in terms of what Generics are for. But feel free to downvote my answer if you think it is crap. – Florian Albrecht Oct 20 '16 at 11:40
  • What I really wanted was a way to use the generic E without any other attribute, but it seems like there's really no way to do it. Thanks anyway guys. – FelipeP Oct 20 '16 at 18:40
  • @FelipeP, well there is, and I described it in my answer, while Denis Kokorin provided links on how to implement it without any library usage, but whatever. – user3707125 Oct 21 '16 at 08:50
  • @user3707125 After checking out what gentyref really is for, I am pretty sure you either did not understand the user's problem, or you do not understand Java Generics. I would appreciate if you change your answer so it uses OP's class and solves the problem - then you will see that gentyref is not the answer here. – Florian Albrecht Oct 21 '16 at 13:31
  • Oh, after reading your code a second time, I understand that you tried to outline a similar class. Please note that your solution only works if subclasses specify **concrete type parameters** for E (or T, in your case). That is **not** (plain) Generics! From a compiler point of view, you are creating a class where you replace `E` with a concrete type. That is no magic, that is loss of flexibility. – Florian Albrecht Oct 21 '16 at 13:34
  • @FlorianAlbrecht, "typeParameterClass is an argument that every other class that **extends AbstractRepository** have to pass, this argument is the class itself, but it does not seem right, since I **already have the class in generic**.", - what am I missing? – user3707125 Oct 21 '16 at 14:02
  • It is nowhere said that extending classes will pass this argument as a **constant**. Instead, they could as well provide that parameter as constructor parameter. Otherwise, the most simple way by far would be to declare an `protected abstract Class getTypeParameter()`. – Florian Albrecht Oct 21 '16 at 14:05
  • @FlorianAlbrecht, what parameter are you talking about? My understanding is that OP was planning to use a construction of form `class SomeRepository extends AbstractRepository {}`, and I wrote a solution for that case. What form of `AbstractRepository` usage do you see? – user3707125 Oct 21 '16 at 14:12
  • `class ConcreteRepository extends AbstractRepository` – Florian Albrecht Oct 21 '16 at 14:16
  • @FlorianAlbrecht, in my opinion phrase "already have the class in generic" removes any possibility of wildcard presence. – user3707125 Oct 21 '16 at 14:29
  • 1
    I think without any usage example of the OP, it doesn't make sense to discuss this any further. – Florian Albrecht Oct 21 '16 at 14:33
  • I updated my question with an example of how I'm doing it now. Just to clarify, what I want is to know if there's a way of using the generic E inside methods, instead of getting the class using any other way, like I did passing the class in constructor. – FelipeP Oct 21 '16 at 15:56
-1

I am using exactly the same approach at my project, and I solved this issue with the help of Gentyref for clearer code (maven, googlecode):

import static com.googlecode.gentyref.GenericTypeReflector.getExactSuperType;
import static com.googlecode.gentyref.GenericTypeReflector.getTypeParameter;

public abstract class GenericRepository<T> {

    protected GenericRepository() {
        Type genericRepositoryType = getExactSuperType(getClass(), GenericRepository.class);
        Type rawItemType = getTypeParameter(genericRepositoryType, GenericRepository.class.getTypeParameters()[0]);
        //noinspection unchecked
        this.modelType = (Class<T>) GenericTypeReflector.erase(rawItemType);
     }
}

You can avoid gentyref usage - its implementation is pretty straightforward, however it's much easier to write this kind of code with it.

user3707125
  • 3,394
  • 14
  • 23