Oliver Wheatley
Oliver Wheatley

Reputation: 21

How to check if a class is capable of implementing an interface at runtime in Java (JDK 17)

I need to determine whether a class is capable of implementing an interface when it does not explicitly implement that interface. I would like to determine this at runtime, I am using reflections to gather classes.

For example:

Lets say I have the following class:

public class Clazz {
    public void doSomething() {
        // ... do it
    }
}

And the following interface:

public interface ClazzInterface {
    public void doSomething();
}

Clazz does not implement ClazzInterface, however it is compatible with ClazzInterface and could implement it without any modification. I am looking for a way to check this at runtime.

Something like:

boolean canImplement = Clazz.class canImplement ClazzInterface.class

Is this possible either through a built in library or method or some logic I could write myself?

I have tried using reflections isAssignableFrom and this does not work, returning false for the above example.

Upvotes: -1

Views: 221

Answers (2)

Turing85
Turing85

Reputation: 20185

Problem statement

OP clarified in the comments that the goal is to build an evaluation system for exercises given to students, I see some approaches that do not require reflection.

Approach 1: return Object

We can release the interface-definition to the students, but replace the return-type with Object. We can then check the runtime-type of the return value via instanceof or Class::isAssignableFrom (docs.oracle.com) (or really any other way we see fit).

Approach 2: Modify the source code to add implements <interface>

There are many way we could do this, e.g. a simple bash-script like

find \
  src/main/java \
  -type f \
  -name "*.java" \
  -exec \
    sed \
      -i \
      's/^\(.*class\s\+Bar\)\s\+{.*$/\1 implements Foo {/g' \
    {} \;

We can then incorporate this script in a plethora of ways in our build-process. The downside of this approach is that it is OS-depndent.

I would like to highlight another approach using OpenRewrite (docs.openrewrite.org), which is OS-independent. OpenRewrite allows us, among other things, to formulate simple rules for refactoring in a declarative way. In our example, we want to replace the string class <classname> with class <classname> implements <interfacename>. If we assume the name of the interface to be Foo and the name of the class to be Bar, a OpenRewrite recipe may look like this:

type: specs.openrewrite.org/v1beta/recipe
name: de.turing85.AddFooInterfaceToBarClass
displayName: Add the Foo interface to the Bar class.
recipeList:
  - org.openrewrite.text.FindAndReplace:
      find: (?<classdef>class\s+Bar)\s+\{
      replace: ${classdef} implements Foo {
      regex: true
      filePattern: src/main/java/**/*.java

Since OpenRewrite provides a maven- (docs.openrewrite.org) and a gradle-plugin (docs.openrewrite.org), we can simply incoroprate it in our build-process.

A POC can be found in this github.com repository.

Upvotes: 1

Elliott Frisch
Elliott Frisch

Reputation: 201439

There is no such concept of "is capable of implementing" an interface. A class either does (or does not) implement an interface. However, you can use reflection to dynamically call some method. It has caveats and limitations (of course). For example, it's usually slow. But here is a doSomething with "duck-typing".

public static final void doSomething(Object o) {
    if (o == null) {
        return;
    }
    Class<?> cls = o.getClass();
    try {
        Method m = cls.getMethod("doSomething", new Class<?>[] {});
        if (m != null) {
            m.invoke(o, new Object[] {});
        }
    } catch (final Exception e) {
        e.printStackTrace();
    }
}

Note the line if (m != null), that does check if doSomething is available in the actual instance o. You could test for the presence of all necessary methods in the same manner.

Upvotes: 3

Related Questions