Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 338
Eric Cornelson Avatar asked Sep 20 '12 15:09

Eric Cornelson


People also ask

How do you find the type of generic type?

Use the IsGenericType property to determine whether the type is generic, and use the IsGenericTypeDefinition property to determine whether the type is a generic type definition. Get an array that contains the generic type arguments, using the GetGenericArguments method.

Can a generic class implement an interface?

Only generic classes can implement generic interfaces. Normal classes can't implement generic interfaces.

How do I get a class instance of generic type T?

The short answer is, that there is no way to find out the runtime type of generic type parameters in Java. I suggest reading the chapter about type erasure in the Java Tutorial for more details. A popular solution to this is to pass the Class of the type parameter into the constructor of the generic type, e.g.


2 Answers

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>.

like image 138
Brian Avatar answered Sep 28 '22 09:09

Brian


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.

like image 34
Saintali Avatar answered Sep 28 '22 09:09

Saintali