2

I call a method with a generic return type like this:

public static <T extends HotelBean> List<T> fill(Class<T> c) throws InstantiationException, IllegalAccessException {
    List<T> list = new ArrayList<>();
    for (int i = 0; i <= 2; i++) {
        T t = c.newInstance();
        list.add(t);
    }
    return list;
}

Is there a way to create an instance inside this method without giving the Class as method paramter? (Class<T> c)

5
  • Probably not in a method like this due to type erasure. There's also no way for the compiler to infer the actual type of T. The only way to get rid of the parameter would be to have the type of T be defined in some reflection data, i.e. a field definition or class with a concrete type. That, however, would probably contradict the purpose of your method or at least not be easier than passing a parameter. Commented Jun 10, 2016 at 11:37
  • No, generic types are erased (docs.oracle.com/javase/tutorial/java/generics/erasure.html) so only thing which Java could use at runtime to determine type of instance it should create comes from Class. Commented Jun 10, 2016 at 11:38
  • You could also pass a Supplier<T> instead, using a MyType::new method reference as an argument. That would at least get rid of the throws clause Commented Jun 10, 2016 at 11:52
  • @JornVernee can you provide an example for using Supplier<T> ? I am very interested of your solution. Commented Jun 13, 2016 at 11:38
  • @Patrick I'll make an answer Commented Jun 13, 2016 at 11:59

2 Answers 2

3

No, unfortunately, there is not. This is the primary reason behind having to pass Class<T> in the first place.

Java compiler strips generic information from the compiled classes and methods in the process known as type erasure. By the time the compiler is done, all Ts become Objects or the lower bound type (this would correspond to HotelBean in your example). Without the access to Class<T> the method is not capable of restoring the type parameter, which is necessary to produce a new instance.

Note that your example would have to pass the desired type one way or the other, because you are populating a list of base class objects with objects that could be HotelBean's subclasses.

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for your answer. Is there a better way of implementaion or should I go on with this approach?
Even without type erasure the compiler would have no way of infering the type of T so you'd have to either use HotelBean which would make generics superflous or tell the method e.g. by calling it like this.<SpecialHotelBean>fill() - which doesn't seem preferable over fill( SpecialHotelBean.class ).
@Patrick I would not try inventing a different approach, because what you have is the idiomatic solution for building generic code for object creation. Other programmers reading your code would instantly recognize it, and understand what you are trying to do.
@Thomas This makes sense, thanks for the comment. Although there are situations when the compiler could infer the type, for example, when you assign to List<SpecialHotelBean> or call a method that takes List<SpecialHotelBean> parameter, the precise type has to be supplied in order for the method to work.
Yeah those are the situations that break due to type erasure.
|
1

Instead of passing a Class to create an instance with, which means having to deal with catching or throwing exceptions, you could pass a Supplier<T> instead.

java.util.function.Supplier<T>, (a Java 8 feature), is a functional interface that has one method get, which returns an object of the type T.

Using a Supplier<T> would look like this:

public static <T extends HotelBean> List<T> fill(Supplier<T> supp) { // No more exceptions!
    List<T> list = new ArrayList<>();
    for (int i = 0; i <= 2; i++) {
        T t = supp.get();
        list.add(t);
    }
    return list;
}

You can pass method references that take no arguments and return an object of type T, as a supplier, which includes a default constructor of T.

class MyClass {
    public MyClass() {...}
}
fill(MyClass::new);

The main difference with newInstance(), is that the object returned from a Supplier<T> is not necessarily a new object, because you don't have to necessarily pass MyClass::new. You could pass any method that takes no arguments and returns a T.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.