I was looking at a way to optimize some things in our code base with generic functions. There is a function with return type List<Object>
which could have return type List<SpecifiedType>
.
Bellow is a minimalist version of that function called function
. It takes a parameter type, based on it calls a corresponding function (here limited to String
) or generic function.
public static ArrayList<String> forString(){
ArrayList<String> res = new ArrayList<>();
// Fetching and processing data specific to String
return res;
}
public static <T> ArrayList<T> forGeneric(Class<T> type){
ArrayList<T> res = new ArrayList<>();
// Fetching data
return res;
}
public static <T> ArrayList<T> function(Class<T> type){
if(type == String.class)
return (ArrayList<T>) forString();
return forGeneric(type);
}
The target is for function above to be called like this: ArrayList<SomeType> someTypes = function(SomeType.class);
Two things I've noticed about the code above:
Cast to ArrayList<T>
is required even though we know that if type String
is passed as a parameter it will return ArrayList<String>
just like forString()
method
Cast to ArrayList<T>
gives Unchecked cast
warning, even though the return type will be ArrayList<String>
My question is is there some better way to do so (preferably without the casts) and if not, then why
First off, this statement is logically wrong
if(type.isInstance(String.class))
If type
is Class<String>
then isInstance
is checking to see if the argument is a string instance. The argument you are passing is a class instance (specifically, a Class<String>
).
If you prefer,
String.class.isInstance(String.class) == false
What you meant was
if(type == String.class)
However, even with this logical error resolved, your code will still have an unchecked cast warning.
The part you are missing is right here
Cast to
ArrayList<T>
is required even though we know that if type String is passed as a parameter it will returnArrayList<String>
just likeforString()
method
Exactly. We know it. But what we know and what the compiler knows are two different things. The compiler is not clever enough to check the conditional and realise that the type is okay. It conceivably could be smart enough, but it is not.
This is precisely why this manifests as a warning and not as an error. It is a warning because what you are doing is potentially wrong; it is not definitely wrong, else it would not compile at all. In this case, the warning should act as a prompt for you to double-check that what you're doing is correct and then you can happily suppress it.
@SuppressWarnings("unchecked")
public static <T> ArrayList<T> function(Class<T> type){
if(type == String.class)
return (ArrayList<T>) forString();
return forGeneric(type);
}
Finally -- and it may be an artifact of your contrived example -- but all of these methods are useless. There does not seem to be any advantage over calling new ArrayList<>()
directly. At runtime, the actual instances are identical regardless of which of the 3 methods it came from.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With