2

I have an method to get list of contacts from database. It return a List

 public List<String[]> getContacts(String param) {
      ...
      Query q = this.getCurrentSession().createSQLQuery("
                   select first_name, last_Name, email_address, fax 
                   from contacts 
                   where first_name = :param");
      q.setParameter("param", param);
      List<String[]> listResult = q.list(); 
      return listResult;
 }

When I get Contact name using following code:

 List<String[]> contacts = (ArrayList<String[]>) contactManager.getContacts(fistName);
 if (contacts != null && contacts.size() > 0) {
      person.setLastName(contacts.get(0)[1]); //error line
 }

I get the error:

java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;

I change source code to:

 List<String[]> contacts = (ArrayList<String[]>) contactManager.getContacts(fistName);
 if (contacts != null && contacts.size() > 0) {
      Object[] objTemp = contacts.get(0);
      String temp = (String) objTemp[1];
      person.setLastName(temp);
 }

It runs well!

I have debugged and the object returned from getContacts() function is still the List. Look like object just cast to List at that time the error line run.

Please help me to explain why the second code can run without error. I think 2 sources are same logic.

Phi
  • 153
  • 2
  • 3
  • 12
  • http://stackoverflow.com/questions/1115230/casting-object-array-to-integer-array-error http://stackoverflow.com/questions/1018750/how-to-convert-object-array-to-string-array-in-java – kan May 09 '14 at 09:33
  • [Type Erasure!](http://docs.oracle.com/javase/tutorial/java/generics/erasure.html) – Dan Temple May 09 '14 at 09:43

2 Answers2

3

In here you try to cast explicitly :

List<String[]> contacts = (ArrayList<String[]>) contactManager.getContacts(fistName)

but when you execute this:

contacts.get(0)[1]

it is first converting the runtime type of the array, which is Object[], to String[], like in:

Object[] colors = { "red", "green", "blue" };
String[] colorsStrings = (String[]) colors;   // -> class cast exception

You get:

java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;

which correctly tells you were trying to cast an array of Objects (Object[].class) to an array of Strings (String[].class).

So you need to explicitly fetch the Object[] first out of the list, then cast the individual item from Object to String, as in out last example.

Another option if you are using Java 8, would be:

String[] contacts = Arrays.stream(contactManager.getContacts(fistName))
                          .toArray(String[]::new);
guido
  • 18,864
  • 6
  • 70
  • 95
  • I thought Java arrays were covariant? – awksp May 09 '14 at 09:26
  • @user3580294 covariance in this case does not apply (my statement about inheritance was incorrect indeed); that applies when you want to pass a String[] parameter to a method which accepts Object[], or when an assignement is executed. String[] is a subtype of Object[] but that does not mean you can cast an Object[] (containing String instances) to String[] – guido May 09 '14 at 09:41
  • This `List contacts = (ArrayList) ` is redundant and do not have any influence on the code. – Damian Leszczyński - Vash May 09 '14 at 09:49
  • @guido That makes sense. I hadn't considered that that was the case. – awksp May 09 '14 at 10:00
1

The problem is that you do not follow the warnings. Everything start here:

 public List<String[]> getContacts(String param) {

      Query q = this.getCurrentSession().createSQLQuery("select first_name, last_Name, email_address, fax from contacts where first_name = :param");
      q.setParameter("param", param);
      List<String[]> listResult = q.list(); 
      return listResult;
 }

List<String[]> listResult = q.list(); this should give you a warning

The expression of type List needs unchecked conversion to conform to List<String[]>

We can replace that with some example code what is running under the hood.

public List getContacts(String param) {

Object[] data = new Object[3];

data[0] = "String";

List result = new ArrayList<>();

result.add(data);

return result;

}

As you see hibernate create a list without defined type and put into it an array of Object[].

As Java during compilation remove all generic parameters your code look more like this.

 public List<Object[]> getContacts(String param) {

      Query q = this.getCurrentSession().createSQLQuery("select first_name, last_Name, email_address, fax from contacts where first_name = :param");
      q.setParameter("param", param);
      List<Object[]> listResult = q.list(); 
      return listResult;
 }

and

List<Object[]> contacts = (ArrayList<Object[]>) contactManager.getContacts(fistName); //Note this cast is not required
 if (contacts != null && contacts.size() > 0) {
      person.setLastName(contacts.get(0)[1]); //error line
 }

In consequence you pass a Object to method that expect String. That is why when you unwrap it and cast manually it works.

Use Dileep proposed solution

List<String> listResult = q.setParameter("param", param).setResultTransformer(Transformers.aliasToBean(String.class)).list();