How to check if a generic type implements a specific type of generic interface in java?

85,627

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.

Share:
85,627
Eric Cornelson
Author by

Eric Cornelson

Just another software developer =)

Updated on August 25, 2020

Comments

  • Eric Cornelson
    Eric Cornelson over 3 years

    Possible Duplicate:
    Generic type of local variable at runtime

    I'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>?