1

I'm attempting to convert the following method:

public List<AliasIp> getStoreAsList(String storeName) {
    return new java.util.ArrayList<>(Arrays.asList(mGson.fromJson(getStoreRaw(storeName), AliasIp[].class)));
}

To a generic version similar to:

public <T> List<T> getStoreAsList(String storeName) {
    return new java.util.ArrayList<T>(Arrays.asList(mGson.fromJson(getStoreRaw(storeName), T[].class)));
}

But I'm running into troubles with the T[].class which doesn't compile with the error:

"Cannot select from a type variable"

What should the syntax be to avoid that error?

Baker
  • 24,730
  • 11
  • 100
  • 106
  • 1
    You face the "type erasure" of Java. Take a look at http://stackoverflow.com/questions/529085/how-to-create-a-generic-array-in-java, it's not 100% equal to your uestion, but in the answer to this question you can read why compiler can't understand what T[].class is. – AGV Feb 07 '16 at 10:43
  • 1
    When Java process you code, it is always remove `T` expression with definite class (type erasure). Java never leaves `T` as is. So, when it comes across `T[]`, it has no idea, what class should be here. I suggest you follow solution of @Kolesnikovich Dmitry, and pass class as method parameter. – Ken Bekov Feb 07 '16 at 10:57
  • By the way `Arrays.asList` already returns a list. There is no need to copy it into an `ArrayList`, unless the method guarantees to return the specific type `ArrayList`, which is not recognizable from its current signature. – Holger Feb 07 '16 at 10:58
  • @Holger the reason for the redundant ArrayList is Arrays.asList is a wrapper around an array and does not allow list.Add() type functionality. See http://stackoverflow.com/questions/1624144/unsupportedoperationexception-for-tarray-aslist-removeint – Baker Feb 07 '16 at 15:05
  • @KenBekov After reading http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ101 I imagine that T is removed during Type erasure as T is used as a Type argument. So that T[].class becomes [].class, which is clearly not a statement. Am I understanding the process correctly? – Baker Feb 07 '16 at 16:14
  • Of course, if your method guarantees to return a `List` which supports adding and removing, then creating a new `ArrayList` is required. However, as said, there’s no indicator that your method does this, so it’s not clear why the caller should assume that. So the best practice is to let the caller, or generally whoever actually need this support, create an `ArrayList` right at the place where necessary. Or change the return type to `ArrayList` to make clear what this method does… – Holger Feb 08 '16 at 08:15
  • @Holger I agree, but I'm also confused. Return type is List and name of method is "AsList" (indicators that method returns a List, an interface which must support add/remove). The caller should assume return type is a List. Yet, without wrapping Arrays.asList inside new ArrayList, add/remove functionality would *not* be available, even though the return type is List. I find that confusing. But, I agree with you that return type could be better as ArrayList, which is more accurate. – Baker Feb 09 '16 at 09:29
  • See, `Collections.unmodifiableList` also returns a `List`, but that list won’t even support `set`. The `List` contract specifies that certain methods are optional, hence, you have to study the documentation of a method about which operations are guaranteed. That’s especially crucial as operations like `stream.collect(Collectors.toList())` currently return an `ArrayList`, but don’t guaranty it, so you *must not* assume mutability; it may change in future versions. The best solution is to never assume mutability if the return type is plain `List` and create mutable lists right when needed. – Holger Feb 09 '16 at 09:53

1 Answers1

6

You can include Class<T[]> klass into method parameters like this:

public <T> List<T> getStoreAsList(String storeName, Class<T[]> klass) {
    return new java.util.ArrayList<T>(Arrays.asList(mGson.fromJson(getStoreRaw(storeName), klass)));
}

Then just use it like this:

List<MyStore1> myStores1 = getStoreAsList("myStoreName1", MyStore1[].class);
List<MyStore2> myStores2 = getStoreAsList("myStoreName2", MyStore2[].class);
Dmitry Kolesnikovich
  • 669
  • 2
  • 15
  • 33