How to check if a generic type implements a specific type of generic interface in java?
Solution 1
Java implements erasure, so there's no way to tell on runtime if genericObject
is an instance of Set<String>
or not. The only way to guarantee this is to use bounds on your generics, or check all elements in the set.
Compile-time Generic Bounds
Using bounds checking, which will be checked at compile-time:
public <T extends SomeInterface> void genericMethod(Set<? extends T> tSet) {
// Do something with tSet here
}
Java 8
We can use streams in Java 8 to do this natively in a single line:
public <T> void genericMethod(T t) {
if (t instanceof Set<?>) {
Set<?> set = (Set<?>) t;
if (set.stream().allMatch(String.class:isInstance)) {
Set<String> strs = (Set<String>) set;
// Do something with strs here
}
}
}
Java 7 and older
With Java 7 and older, we need to use iteration and type checking:
public <T> void genericMethod(T t) {
Set<String> strs = new HashSet<String>();
Set<?> tAsSet;
if (t instanceof Set<?>) {
tAsSet = (Set<?>) t;
for (Object obj : tAsSet) {
if (obj instanceof String) {
strs.add((String) obj);
}
}
// Do something with strs here
} else {
// Throw an exception or log a warning or something.
}
}
Guava
As per Mark Peters' comment below, Guava also has methods that do this for you if you can add it to your project:
public <T> void genericMethod(T t) {
if (t instanceof Set<?>) {
Set<?> set = (Set<?>) t;
if (Iterables.all(set, Predicates.instanceOf(String.class))) {
Set<String> strs = (Set<String>) set;
// Do something with strs here
}
}
}
The statement, Iterables.all(set, Predicates.instanceOf(String.class))
is essentially the same thing as set instanceof Set<String>
.
Solution 2
You don't have that option in Java, sadly. In Java, there is no runtime difference between a List<String>
and a List<Integer>
. It is the compiler that ensures that you never add()
an Integer
to a List<String>
. Even that compiler enforcement is not strict, so you can "legally" do such abominations with unchecked casts....
All in all, for (almost) any matter of runtime-type-identification, you have to take List<String>
for what it actually is: just a raw List
. That is called type erasure.
That said, nothing prevents you from inspecting the contents of a List
for their types:
public boolean isListOf(List<?> list, Class<?> c) {
for (Object o : list) {
if (!c.isInstance(o)) return false;
}
return true;
}
To use this method:
// ...
if (genericObject instanceof List<?>) {
if (isListOf((List<?>) genericObject, String.class)) {
@SuppressWarnings("unchecked")
List<String> strings = (List<String>) genericObject;
}
}
An interesting observation: if the list
is empty, the method returns true for all given types. Actually there is no runtime difference between an empty List<String>
and an empty List<Integer>
whatsoever.
Comments
-
Eric Cornelson over 3 years
Possible Duplicate:
Generic type of local variable at runtimeI'm new to Java generics, and coming from a .NET world, I'm used to being able to write a method like this:
public void genericMethod<T>(T genericObject) { if (genericObject is IList<String>) { //Do something... } }
The method accepts an object of a generic type, and checks whether that object implements a specific version of the generic interface
IList<>
, in this case,IList<String>
.Now, in Java, I'm able to do this:
public <T> void genericMethod(T genericObject) { if (genericObject instanceof Set<?>) { //Do something... } }
BUT
Java does not let me do
if (genericObject instanceof Set<String>)
From what I know, because of type erasure, normally in Java this would be taken care of by a class object, and we would do something like the following:
public <T> void genericMethod(T genericObject) { Class<OurTestingType> testClass = OurTestingType.class; if (genericObject.getClass() == testClass) { //Do something... } }
but since the type I'm checking for is a generic interface, you can't do this:
Class<Set<String>> testClass = Set<String>.class
So, how, in Java, do I check if a generic object implements the specific type of
Set<String>
?