简体   繁体   中英

Passing Class to a method to instantiate a generic

The following code doesn't work, because the marked line does not compile:

MyClass {
    //singleton stuff
    private static MyClass instance;
    private MyClass () {}
    public static MyClass getInstance() {
        if(instance==null) {
            instance = new MyClass ();
        }
        return instance;
    }

    // method creating problems
    public NonGenericSuperClassOfGenericClass create(Class<?> c1, Class<?> c2) {
        return new GenericClass<c1,c2>; // DOES NOT COMPILE!!!
    }
}

I do NOT want MyClass to be a generic. Why? Because the actual implementation of the method create is similar to the following:

    // method creating problems
    public NonGenericSuperClassOfGenericClass create(Class<?>... classes) {
        if(someCondition)
             return new GenericClass<classes[0],classes[1]>;
        else
             return new OtherGenericClass<classes[0]>;
}

Therefore, I cannot couple MyClass to any particular generics. Is there a way to instantiate my GenericClass with passed parameters? How?

Thanks


Thank you for your answers. I'll tell you the whole story.

I'm using Spring and I plan to use MongoDB .

The class GenericClass is something like:

 GenericClass<PersistetType1, Long> 

or

 GenericClass<PersistentType2, Long>

where PersistentType1/2 are classes that I need to finally store in the DB, while, GenericClass is a sort of Proxy to access Mongo APIs. In fact, it looks like:

  public MongoTemplate getTemplate();
  public void save(T toInsert);
  public List<T> select(Query selectionQuery);
  public T selectById(ID id);
  public WriteResult update(Query selectionQuery, Update updatedAttributes);
  public void delete(T toRemove);
  public void delete(Query selectionQuery);

Now, what? From Controllers (or Entity, if you are picky) I need to instantiate the repository and invoke any methods. This causes the Controllers to be coupled with MongoDB, ie they explicitly have to instantiate such GenericClass, which is actually called MongoRepository and is strictly dependent on Mongo (in fact it is a generic with exactly two "degrees of freedom").

So, I decided to create MyClass, that is a further proxy that isolates Controllers. In this way, Controller can get the single instance of MyClass and let it create a new instance of the appropriate repository. In particular, when "somecondition" is true, it means that we want to use MongoRepository (when it is false, maybe, a need to instantiate a Hibernate proxy, ie HibernateRepository). However, MongoRepository is generic, therefore it requires some form of instantiation, that I hoped to pass as a parameter.

Unfortunately, generics are resolved at compile time, thus they don't work for me, I guess.

How can I fix that?

That doesn't make sense, especially in Java.

The whole point of generics is to specify at compile-time what the types are.

If you don't know what the types are until runtime, Java generics would be useless, due to type erasure. (in C#, you would need reflection)

Due to Type Erasure , a first implementation could consider only rawtypes implementation.

// Similar to original method
@SuppressWarnings("rawtypes")
public Map<?, ?> create(Class<?> c1, Class<?> c2) {
    return new HashMap();
}

A cleaner alternative would be the following:

// A more generic alternative
public <S, T> Map<S, T> create2(Class<? extends S> c1, Class<? extends T> c2) {
    return new HashMap<S, T>();
}

Both implementations does exactly the same thing at runtime. The second is better since its signature depends on its arguments (while create returns a Map<?, ?> , the second may return a Map<String, Integer> )

There is no need to pass the Class objects in at runtime. Generics are purely compile-time. Since (it seems like) you are returning a non-generic type, and so you are throwing away the generic parameters anyway (I don't know why you would do this; but since you are, I will go with that), it doesn't matter what you use as the parameter. Any type parameter that passes the type checker will be sufficient:

public NonGenericSuperClassOfGenericClass create() {
    if(someCondition)
         return new GenericClass<Object,Object>();
    else
         return new OtherGenericClass<Object>();
}

One thing to comes to mind is something along the lines of:

public NonGenericSuperClassOfGenericClass create(Class<?>... classes) {
    if(someCondition)
         return new helper1(classes[0],classes[1]);
    else
         return new helper2(classes[0]);
}

public <R,T> GenericClass<R,T> helper1( Class<R> a, Class<T> b ){
    return new GenericClass<R,T>();
}

public <T> OtherGenericClass<T> helper2( Class<T> a ){
    return new OtherGenericClass<T>();
}

The way is to parametrize the generic with another generic: T1>. This way I get the coupling I need. The appropriate choice of AnotherClass gives the expected result.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM