1

When a method is taking List of Object[] as input, even at the run-time the actual input is List of Object, it is still okay for the method. Question is, for generics in Java, what does List<something> really mean and what is the difference between List<Object[]> and List<Object>?


/*
 * if name is "A", then the input is a List<Object[]>
 * if name is "B", then the input actually is a List<Object>
 */
public List<String> convert(List<Object[]> objectList, String name) {

    List<String> resultList = new ArrayList<String>();

    if (StringUtils.equals(name, "A")) {
        for (Object[] object : objectList) {
            String code = StringUtils.join((String) object[0], (String) object[1]);
            resultList.add(code);
        }
        return resultList;
    } 

    else if(StringUtils.equals(name, "B")) {
        for (Object object : objectList) {
            String code = (String) object;
            resultList.add(code);
        }
        return resultList;
    }
}

Actually during the run-time, this method works fine and takes both List<Object[]> and List<Object>.

Just wondering anyone could give some explanation of how this could work.

rgettman
  • 167,281
  • 27
  • 248
  • 326
  • 5
    An Object could be an Object[], but it could also be an Integer, Cow, horse Object etc. – dustytrash Apr 02 '19 at 21:07
  • Didn't try the code but I think what's bothering is how an Object is accepted in Object[] signature. Or more precisely in this case, how a value of type List can pass in a signature of List ? Shouldn't there be a ClassCastException ? ( I can of course understand the opposite, that Object[] value is accepted at Object signature because an array is also an Object but here the signature of the method accepts only ListM). – Pam Stums Apr 02 '19 at 21:15
  • @Pam Stums I was also feeling confused since I was thinking in the same way. To pass a List to a method which is expecting List seems unreasonable to me, but when I was debugging I found it actually works. – Haolin Zhang Apr 02 '19 at 21:54

1 Answers1

2

So to answer in order:

  1. For generics in Java, what does List<something> really mean? The <> bracket notation allows you to specify what specific type of a generic class you want. Basically, a List can be a List<A>, List<B>, etc.: its contents can be anything. Writing List<Q> says that it is a List which contains things of type Q.

  2. What is the difference between List<Object[]> and List<Object>? [] is the array operator. A List<Object[]> is a list containing arrays of Object. A List<Object> is a list containing Objects. In Java, almost everything is an Object, including arrays!

There's another thing to note here: Java generics are complicated (and interesting), and there's a thing called type erasure which means that, at run time, List<Object> and List<XYZ> both become List - no specific type! The things in the brackets are only used at compile time - basically, to check that what you've written is correct. See What does angle brackets T mean in Java and Java generics type erasure when and what happens


Why does your code run? Well, in the "A" case it's straightforward. At runtime, the parameter is just a List, but your code expects to find Object[]s in that list, and it does. In the "B" case it's more complicated: the parameter is just a List, and you iterate over its contents treating them as Object. This is fine because as we said above (almost) everything is an Object. You then take those objects and cast them to String - this is only weird if we don't take into account that at runtime the input List is untyped. As far as Java is concerned, there could be anything in that list, and you tell it that there are Strings. Because the input was in fact a list of String, it all works out.

The interesting bit (which you didn't include in your code) is how you're calling a method that takes List<Object[]> with a List<String> without Java complaining at compile time.

MyStackRunnethOver
  • 3,424
  • 2
  • 21
  • 32
  • Thank a lot for the answer. Another question based on what you said: if the method is defined as "convert(List objectList, String name)" while the actual input is List, then it make sense since array is also an object. But here is the other way around: defined as "convert(List objectList, String name)" but actually takes List. Is there any explanation about it? – Haolin Zhang Apr 02 '19 at 21:49
  • Looking at your code, I think all it means is wherever you **call** `convert()` you're avoiding type checking through some mechanism. If you want, you can update your question to show me that code. The reason the code above runs is because of type erasure: at run-time the argument is a `List` - your method is written to handle the typing: In the `"A"` case, it's straightforward: your loop expects `Object[]`, and it finds it. In the `"B"` case, your loop *treats the contents as `Object`* (which is OK), then **casts those contents to `String`** (which is OK to do on an `Object`, not `Object[]`) – MyStackRunnethOver Apr 02 '19 at 22:04
  • Updated my answer to reflect the above – MyStackRunnethOver Apr 02 '19 at 22:09
  • thanks again for replying. This method is actually called after I catch the result list returned from JPA: Query.getResultList(). The reason I noticed this scenario is because, sometimes I got a List by fetching single one column, while other time I got a List by fetching more than one columns. – Haolin Zhang Apr 02 '19 at 22:44
  • What I understand is, it may happens because in the following way: for Query.getResultList() method, it returns List and thus declaring convert(List – Haolin Zhang Apr 02 '19 at 22:45