views:

4261

answers:

8

For example I have such query:

Query q = sess.createQuery("from Cat cat");
List cats = q.list();

If I try to make something like this it will show warning "Type safety: The expression of type List needs unchecked conversion to conform to List":

List<Cat> cats = q.list();

Is there a way to avoid it?

Thanks.

+1  A: 

In our code we annotate the calling methods with:

@SuppressWarnings("unchecked")

I know it seems like a hack, but a co-developer checked recently and found that was all we could do.

tyshock
A: 

No, but you can isolate it into specific query methods and suppress the warnings with a @SuppressWarnings("unchecked") annotation.

Dave L.
Wrong... Joe Dean is right, you can use the ? as the generic type to avoid the warnings...
Mike Stone
That's not true. If you use a List<?> then you cannot use the elements of the list as Cat's without the unnecessary step of creating a duplicate list and casting each item.
Dave L.
well, if you use the results directly via casting you don't need to create the list, and regardless, the question was "is there a way to avoid it", the answer is most definitely YES (even without supress warnings)
Mike Stone
A: 

If you don't want to use @SuppressWarnings("unchecked") you can do the following.

   Query q = sess.createQuery("from Cat cat");
   List<?> results =(List<?>) q.list();
   List<Cat> cats = new ArrayList<Cat>();
   for(Object result:results) {
       Cat cat = (Cat) result;
       cats.add(cat);
    }

FYI - I created a util method that does this for me so it doesn't litter my code and I don't have to use @SupressWarning.

Joe Dean
That just stupid. You're adding runtime overhead to overcome a completely compiler related problem. Remember that type arguments aren't reified so there is no runtime checking of the type.
John Nilsson
A: 

Joe Dean's solution looks interesting, but do you think it's worth it - create a new List and loop through all elements just to get rid of warnings?

(sorry, can't add a comment directly to his solution for some reason)

serg
Agreed. It seems unnecessary and uglier than just using an annotation to inform the compiler that you know the existing list already contains the correct type.
Dave L.
An alternative is to use the list directly with casting rather than construct the copied list... either way, I tend to prefer avoiding using the annotations if I can, though it is a subjective choice of course and you should do what you like best
Mike Stone
A problem with the supress warning is you won't get a class cast exception until you use the list... if you copy the list, you will get the exception right away... a minor benefit, but still nice (and if your results aren't huge, then the copy won't be prohibitively inefficient)
Mike Stone
+3  A: 

We use @SuppressWarnings("unchecked") as well, but we most often try to use it only on the declaration of the variable, not on the method as a whole:

public List<Cat> findAll() {
    Query q = sess.createQuery("from Cat cat");
    @SuppressWarnings("unchecked")
    List<Cat> cats = q.list();
    return cats;
}
cretzel
+12  A: 

Using a @SupressWarning everywhere, as suggested, is a good way to do it, though it does involve a bit of finger typing each time you call q.list().

There are two other techniques I'd suggest:

Collections.checkedList()

Replace your assignment with this:

List<Cat> cats = Collections.checkedList(Cat.class, q.list());

You might want to check the the javadoc for that method, especially with regards to equals() and hashCode().

Write a cast-helper

Simply refactor all your @SupressWarnings into one place:

List<Cat> cats = MyHibernateUtils.listAndCast(q);

...

public static <T> List<T> listAndCast(Query q) {
    @SuppressWarnings("unchecked")
    List list = q.list();
    return list;
}

Some comments:

  • I chose to pass in the Query instead of the result of q.list() because that way this "cheating" method can only be used to cheat with Hibernate, and not for cheating any List in general.
  • You could add similar methods for .iterate() etc.
Matt Quail
At first glance, the Collections.checkedList(Collection<E>,Class<E>) method looks like the perfect solution. However, the javadoc says that it only prevents incorrectly typed elements from being added through the typesafe view that the method generates. No checking is done on the given list.
phatblat
+1  A: 

We had same problem. But it wasn't a big deal for us because we had to solve other more major issues with Hibernate Query and Session.

Specifically:

  1. control when a transaction could be committed. (we wanted to count how many times a tx was "started" and only commit when the tx was "ended" the same number of times it was started. Useful for code that doesn't know if it needs to start a transaction. Now any code that needs a tx just "starts" one and ends it when done.)
  2. Performance metrics gathering.
  3. Delaying starting the transaction until it is known that something will actually be done.
  4. More gentle behavior for query.uniqueResult()

So for us, we have:

  1. Create an interface (AmplafiQuery) that extends Query
  2. Create a class (AmplafiQueryImpl) that extends AmplafiQuery and wraps a org.hibernate.Query
  3. Create a Txmanager that returns a Tx.
  4. Tx has the various createQuery methods and returns AmplafiQueryImpl

And lastly,

AmplafiQuery has a "asList()" that is a generic enabled version of Query.list() AmplafiQuery has a "unique()" that is a generic enabled version of Query.uniqueResult() ( and just logs an issue rather than throwing an exception)

This is a lot of work for just avoiding @SuppressWarnings. However, like I said (and listed) there are lots of other better! reasons to do the wrapping work.

Pat
A: 

It's not an oversight or a mistake. The warning reflects a real underlying problem - there is no way that the java compiler can really be sure that the hibernate class is going to do it's job properly and that the list it returns will only contain Cats. Any of the suggestions here is fine.

paulmurray