2

Consider the following interface

// src/MyInterface.java

interface MyInterface {
    public void quack();
}

which is used in the following application dynamically; i.e. its implementation is loaded dynamically—for demonstration purposes we'll just use the implementing class' name to determine which implementation to load.

// src/Main.java

class Main {
    public static void main(String[] args) {
        try {
            MyInterface obj = (MyInterface) Class.forName("Implementation")
                                                 .getDeclaredConstructor()
                                                 .newInstance();
            obj.quack();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

The following implementation of the interface is available:

// src/Implementation.java

class Implementation implements MyInterface {
    public void quack() {
        System.out.println("This is a sample implementation!");
    }
}

As I would intuitively think, MyInterface provides information that is only relevant at compile-time, such as which methods can be invoked on objects that implement it, but it shouldn't be needed at runtime, since it doesn't provide any "executable code". But this is not the case: if I try to run the compiled Main.class without MyInterface.class, it complains:

$ javac -d bin/ src/*

$ rm bin/MyInterface.class

$ java -cp bin/ Main
Exception in thread "main" java.lang.NoClassDefFoundError: MyInterface
[...]

I guess it makes sense because it needs access to the MyInterface's Class object to perform the cast to MyInterface, so it needs to load MyInterface. But I feel there should be a way to make it a compile-time only dependency. How?


Some context

This question arose when I learned that there can be compile-time only dependencies, an example of which is the servlet api. I read that when compiling servlet code, you need to have the servlet-api (in Tomcat's case) jar, but at runtime it is not needed because the server provides an implementation. Since I didn't understand exactly how that could work, I tried setting up the little experiment above. Did I misunderstand what that means?


Edit: this Gradle page mentions that a compile-time only dependency could be

Dependencies whose API is required at compile time but whose implementation is to be provided by a consuming library, application or runtime environment.

What would be an example for that? I find that sentence a bit confusing, because it seems to imply that the API is not needed at runtime, and only the implementation is. From the answers, I gather that's not possible, right? (Unless somehow implementing a custom classloader?)

Anakhand
  • 2,838
  • 1
  • 22
  • 50
  • 1
    Addressing your edit: The Gradle excerpt is talking about the same thing as Maven "provided" dependencies. Again, it means you don't have to include the dependency with your application because the target container already includes the dependency. The dependency is present at runtime regardless. – Slaw Jan 01 '20 at 23:21

2 Answers2

1

Yes, looks like you misunderstood example with servlet-api.jar. You need it in your project as a compile time dependency because Tomcat comes itself with that jar and that jar will be added to runtime classpath by Tomcat. if you use classes/interfaces in your code they should be somehow added to classpath since your code depends on them.

And starting Java 8 interfaces can have default implementations for methods ("executable code") and interfaces also can have constants.

Maybe it is possible to run application without interface declaration but in that case you need to develop your custom Classloader which will check for interface implementation and load it instead of interface itself.

Ivan
  • 8,508
  • 2
  • 19
  • 30
  • How would that custom classloader be written in that case? – Anakhand Dec 31 '19 at 19:08
  • 1
    Frankly speaking I have no idea since I've never implemented classloader. Also as I've said even this will not work if your interface has constants/default methods – Ivan Dec 31 '19 at 19:10
  • @Anakhand I don't think a custom class loader could allow one to just not include an interface while expecting an implementing class to function correctly. The class file format (see [§4 of the _Java Virtual Machine Specification_](https://docs.oracle.com/javase/specs/jvms/se13/html/jvms-4.html)) only contains a single class and simply records which interfaces are implemented by name. The JVM needs to know the interface, not just its name, in order to load the class. – Slaw Jan 01 '20 at 23:38
1

Did I misunderstand what that means?

Yes.

You're talking about "provided" dependencies (at least, that's what Maven calls them). Such a dependency still must be present on the classpath/modulepath at both compile-time and runtime. However, you don't have to include the provided dependency with your application when deploying your application, because the target container/framework already includes the dependency.

Slaw
  • 37,820
  • 8
  • 53
  • 80