111

I have a Class object. I want to determine if the type that the Class object represents implements a specific interface. I was wondering how this could be achieved?

I have the following code. Basically what it does is gets an array of all the classes in a specified package. I then want to go through the array and add the Class objects that implement an interface to my map. Problem is the isInstance() takes an object as a parameter. I can't instantiate an interface. So I am kind of at a loss with this. Any ideas?

Class[] classes = ClassUtils.getClasses(handlersPackage);
for(Class clazz : classes)
{
    if(clazz.isInstance(/*Some object*/)) //Need something in this if statement
    {
        retVal.put(clazz.getSimpleName(), clazz);
    }
}
Hash
  • 4,647
  • 5
  • 21
  • 39
user489041
  • 27,916
  • 55
  • 135
  • 204

5 Answers5

259

You should use isAssignableFrom:

if (YourInterface.class.isAssignableFrom(clazz)) {
    ...
}
Graham Russell
  • 997
  • 13
  • 24
Flavio
  • 11,925
  • 3
  • 32
  • 36
  • This works if the project is the same. But if you copy the interface code 1:1, make a new project and jar, then try to load that jar as a plugin, the call will return false. Comparing by name then "works", like Roddy posted. But I have no idea how to check this in the way Java eventually verifies the compatibility. By name is a dirty approach. Yours is fine, of course, if the project is the same. ................ MAYBE I'm doing it wrong: I create a URLClassLoader instance for the plugin file and load it like that. Maybe I should try a different class loader. – Dreamspace President Jul 19 '18 at 14:00
  • 5
    You're having class loading issues. If you load the same class twice with different class loaders, the two `Class` instances will not be compatible. You could see errors like `java.lang.ClassCastException: com.my.CustomClass cannot be cast to com.my.CustomClass` or something similarly inexplicable. – Flavio Jul 20 '18 at 11:57
  • I have by now tried various approaches, and in the end, it turned out that *the main problem* I had was this: While the interface in my plugin and main project were identical, they were not in the same place, so the namespace/address was a different one. Btw., I'm now using: `myClassLoader = new URLClassLoader(new URL[] { candidateFile.toURI().toURL() }, LoadedPlugin.class.getClassLoader());` and `classToLoad = Class.forName("com.blablabla.plugin.Main", true, myClassLoader);` and `instance = (MyIntf) classToLoad.newInstance();` Works like a charm. – Dreamspace President Jul 21 '18 at 13:16
19

you can use the below function to get all the implemented interfaces

Class[] intfs = clazz.getInterfaces();
Ankur
  • 12,676
  • 7
  • 37
  • 67
11

You can use class.getInterfaces() and then check to see if the interface class is in there.

Class someInterface; // the interface you want to check for 
Class x; // 
Class[] interfaces = x.getInterfaces();

for (Class i : interfaces) {
    if (i.toString().equals(someInterface.toString()) {
        // if this is true, the class implements the interface you're looking for
    }
}
Roddy of the Frozen Peas
  • 14,380
  • 9
  • 49
  • 99
  • This approach will technically work, but the much simpler and cleaner approach is to use `isAssignableFrom` as Flavio mentions. – jwj Aug 16 '17 at 17:20
  • Yes, that's true, although your answer has been upvoted more than a few times and I thought it would be useful to add some context. While using `isAssignableFrom` is probably preferable, there might be instances where you need to scan the list of interfaces a class implements by looking at the names. – jwj Aug 16 '17 at 20:41
  • 2
    This actually doesn't work, getInterfaces() only works if the class implements the interface directly, if a superclass implements the interface, or a super interface extends it, that interface won't be returned by getInterfaces(). You need to traverse the tree of all super classes and interfaces in order to get all the interfaces that the class implements. – James Roper Sep 28 '18 at 02:57
  • That wasn't the question though. – Roddy of the Frozen Peas Sep 28 '18 at 03:04
1

You can also set the instance adding ".class"

Class[] classes = ClassUtils.getClasses(handlersPackage);
for(Class clazz : classes)
{
    if(Interface.class.isAssignableFrom(clazz))
    {
        retVal.put(clazz.getSimpleName(), clazz);
    }
}
Manu Navarro
  • 700
  • 6
  • 9
  • 4
    For anyone looking at this approach, please consider Flavio's answer. Note that the code in this example does a few things that might not make immediate sense: `ClassUtils` is not part of Java (it's in Guava or Spring and other frameworks), the term `Interface` as used above is meant to refer to a specific interface being tested (i.e., it's not a Java keyword in this context), and the purpose of `retVal` is not explained or mentioned anywhere. – jwj Aug 16 '17 at 17:23
0

A contribution for all the other answers, when possible do not use the most upvoted answer of method isAssignableFrom, even the "not great" answer of using clazz.getInterfaces() has better performance than isAssignableFrom.

A common mistake for developers when looking for an answer to the OP question, is to prefer isAssignableFrom when an instance is available, wrongly doing this:

if (IMyInterface.isAssignableFrom(myObject.getClass())) {
    ...

When possible, use IMyInterface.class.isInstance or instanceof as both of those have way better performance. Of course, as the OP stated; they have the drawback that you must have an instance and not just the class.

if (IMyInterface.class.isInstance(myObject)) {
    ...
if (myObject instanceof IMyInterface) { // +0.2% slower than `isInstance` (*see benchmark)
    ...

An even faster, but ugly solution would be to store an static Set with all the "valid" classes instead of checking them, this ugly solution is only preferred when you need to test classes a lot, as its performance outperforms all the other approaches for direct class check.

public static final Set<Class<?>> UGLY_SET = Stream.of(MyClass1.class, MyClass2.class, MyClass3.class).collect(Collectors.toCollection(HashSet::new));
if (UGLY_SET.contains(MyClass)) {
    ...

(*) JMH Benchmark for +0.2%

Please visit this answer from users @JBE, @Yura and @aleksandr-dubinsky, credits for them. Also, there's plenty of detail in that answer for the benchmark results to not be valid, so please take a look into it.

luiscla27
  • 4,956
  • 37
  • 49
  • Do you have numbers to back this up? Performance is complicated, and an expert said that [`Class.isInstance` is faster than checking against a list](https://shipilev.net/blog/2015/faster-atomic-fu/#_class_isinstance_performance). – Johannes Kuhn Mar 07 '22 at 22:42
  • You might be right as I was focusing my tests against `isAssignableFrom`. However, by reading that article, I would expect the same behaviour from the compiller for a final static map than the behaviour that blogger describes from the `isInstance` method, BTW, I've modified the code from a `List` to a `Set` as it has also "better" performance than lists.... (because internally uses a map), whatever the case, I was already planning to run a benchmark test with all this, I'll post the results. thank you so much for asking! – luiscla27 Mar 07 '22 at 23:53
  • I personally think you should use the easiest thing to do the job. If you have an object and the class is known at compile time: `instanceof`. Class is not known at compile time and object: `Class::isInstance`. No object, classes not known at compile time: `Class::isisAssignableFrom`. Both classes known at compile time: Do it by hand. – Johannes Kuhn Mar 08 '22 at 00:23
  • 1
    Also, as an other note: As in the article mentioned it depends if methods can be inlined or not - so any general statement about performance (without taking inlining/constant folding into account) will probably be wrong in at least one case. – Johannes Kuhn Mar 08 '22 at 00:25