13

Methods that are generic using the T parameter can for sure be handy. However, I am curious what the use of a generic method would be if you pass an argument such as Class<T> clazz to the method. I've come up with a case that maybe could be an possible use. Perhaps you only want to run a part of the method based on the type of class. For example:

/** load(File, Collection<T>, Class<T>)
* Creates an object T from an xml.  It also prints the contents of the collection if T is a House object.
* @return T
* Throws Exception
*/
private static <T> T void load(File xml, Collection<T> t, Class<T> clazz) throws Exception{
    T type = (T) Jaxb.unmarshalFile(xml.getAbsolutePath(), clazz);  // This method accepts a class argument.  Is there an alternative to passing the class here without "clazz"?  How can I put "T" in replace of "clazz" here?
    if (clazz == House.class) {
         System.out.println(t.toString());
    } else {
         t.clear();
    }
    return T;
}

Is this an accepted practice? When is the Class<T> clazz argument useful with generic methods?

Stephen D
  • 2,836
  • 4
  • 27
  • 40

7 Answers7

13

Is this an accepted practice?

Well, to me.. no not really. To me, it seems somewhat pointless when you can simply define some boundaries on the type of T. For example:

private static <T extends House> void load(Collection<T> t)

This will guarantee that either the object is of type House or of a subclass of House, but then again if you only want an instance of type House or it's subclasses, it should really just be:

private static void load(Collection<House> houses)

The idea of generics is to make a method or a class more malleable and extensible, so to me it seems counter-intuitive to start comparing class types in the method body, when the very notion of generics is to abstract away from such details.

christopher
  • 26,815
  • 5
  • 55
  • 89
4

I'd only pass class objects if the generic type could not be derived otherwise. In your case, the compiler should be able to infer T from the collection. To treat specific objects differently, I'd use polymorphism - e.g. House#something() and Other#something(), and just call anyObject.something().

Fabian Steeg
  • 44,988
  • 7
  • 85
  • 112
4

I think it is acceptable but if it can be avoided then you should. Typically, if you can have different methods which accepts different type, then do it instead of one method which uses if clauses to do something different depending on the type of the parameter. You could also delegates to the class the operation you want to make specific for a given type.

In your case, you could simply test the type of each element of the collection using instanceof, to do what you need for the specific type. But it won't work if the list is empty.

A typical use is if you need to get the type to create it and you can find it from another way. For instance, Spring uses it to load a bean from its name:

<T> T getBean(Class<T> requiredType)

In that case, it cannot be avoided (without having to cast).

LaurentG
  • 11,128
  • 9
  • 51
  • 66
  • 2
    One does not "simply test the type of an element of the collection using `instanceof`". This method won't work if the collection is empty, and might give false result due to polymorphism - a `Collection` can contain an `House`, and a `Collection` can contain subclasses of `House`. – Idan Arye Aug 22 '13 at 13:05
  • 1
    Another example is the Criteria API in Hibernate – blank Aug 22 '13 at 13:18
  • 1
    In particular, the JCR API gives a nice set of examples for how this can be done with its [PropertyMap class' get method](http://sling.apache.org/apidocs/sling5/org/apache/sling/jcr/resource/JcrPropertyMap.html). They offer a version where you pass in a Class, as well as a version where you simply offer up a default value of type (should there not be any value for the given key). This, in my opinion, is a much more elegant solution, while also nicely handling error conditions. – Alexis Beingessner Aug 22 '13 at 15:25
4

If the returned value or other parameters types are dependent or need to be equal, generics will add compile time checks, so that there's no need to cast to T.

Examples

<T> T createNewInstanceOfType(Class<T> type);


<T> void addValueToCollection(Collection<T> collection,T value);


<T> List<Class<? extends T>> findSubClassesInClasspath(Class<T> superType);

Raw types

It is still possible to defer a casting error until runtime (ClassCastException) with some casts, e.g. with implicit casts from non-generic (raw) types to generic ones:

List nonGenericList = new ArrayList();
nonGenericList.add(new Integer(42));
List<String> wreckedList = nonGenericList;

The compiler will generate a bunch of warnings, unless you suppress them with annotations or compiler settings.

Compiler Settings (Eclipse):

For example, the usage of raw types generates a warning per default, one can treat warnings as errors and even as fatal errors:

enter image description here

Sam
  • 7,778
  • 1
  • 23
  • 49
  • Is it always the case that the compiler will generate warning for these cases? If this is the problem one runs into, should they not use generics here? – Stephen D Aug 22 '13 at 13:27
  • AFAIK it will, unless you suppress them as described above in a recent edit. – Sam Aug 22 '13 at 13:28
  • And yes, one shouldn't use the 'raw version' of generic types, as the compiler warnings suggests. Generics were introduced with J2SE 5.0, (older) code with non-generic declarations will cause the 'Usage of a raw type' warning, if the corresponding, new generic types are available. – Sam Aug 22 '13 at 13:53
4

You would pass a Class<T> argument in generics if, and only if, you would pass a Class argument before generics. In other words, only if the Class object is used in some way. Generics serves as a compile-time type checking tool. However, what arguments you pass should be determined by the runtime logic of the program, and should be irrelevant of generics.

newacct
  • 119,665
  • 29
  • 163
  • 224
2

I haven't seen passing a Class object in order to check the runtime type of an object as a common use case for generics. If you're doing that, there's a good chance that there's a better way to set up your class structure.

What I have seen is if you need to create a new instance of the class in question, or otherwise use reflection. In that case you do have to pass the Class object, because Java cannot derive it at runtime thanks to type erasure.

Taymon
  • 24,950
  • 9
  • 62
  • 84
1

In your case actually having the Generic parameter is not strictly needed. Since the output of the function you are describing does not depend on the type of the input you might as well use wild cards.

private static void stuff(Collection<?> t){
    Object next = t.iterator().next(); //this is ugly and inefficient though
    if(next instanceof House){  
        System.out.print(next.toString());
    }else{
        t.clear();
    }
}

The only time you should use generic parameter is when the type of the result of a function will be dependent of the type of the parameters.

You will need to pass the Class corresponding to the type when your code will need it; most of the time this happens when: - You need to cast/type check objects to T - There is serialization/deserialization involved. - You cannot access any instance of T in your function and you cannot call the getClass() method when you need it.

Passing a Class on every generic function will result in you passing an unnecessary parameter most of the time, which is regarded as bad practice.

I answered a similar discussion in the past: When to use generic methods and when to use wild-card?

Community
  • 1
  • 1
le-doude
  • 3,345
  • 2
  • 25
  • 55