When trying to write a library (more on the why I need to do this at the bottom of the post), I got blocked by an unexpected behavior in the Kotlin compiler. In Java this works, and I can’t figure out why it shouldn’t work in Kotlin.
Minimal examples
All classes in the examples are in the same package (omitted for brevity).
First the Java version, which compiles and runs fine:
// ** foo-utils Maven artifact **
// The Foo class is excluded from the artifact (using maven-jar-plugin)
// Foo.java
class Foo {}
// FooUtils.java
class FooUtils {
public static void receiveFoo(Foo foo) {}
}
// ** bar Maven artifact **
// Depends on foo-utils
// Foo.java
class Foo {}
// App.java
public class App {
public static void main(String[] args) {
FooUtils.receiveFoo(new Foo());
}
}
Now the Kotlin version, which give a compilation error:
// ** foo-utils Maven artifact **
// The Foo class is excluded from the artifact (using maven-jar-plugin)
// Foo.kt
class Foo {}
// FooUtils.kt
object FooUtils {
fun receiveFoo(foo: Foo) {}
}
// ** bar Maven artifact **
// Depends on foo-utils
// Foo.kt
class Foo {}
// App.kt
object App {
@JvmStatic
fun main(args: Array<String>) {
// COMPILATION ERROR: Cannot access class 'org.example.Foo'. Check your module classpath for missing or conflicting dependencies
FooUtils.receiveFoo(Foo())
}
}
Is this expected? Why? Is there a way to make it work like in Java?
Why I need to do this
Kotlin projects that uses Protobuf will have Kotlin classes (generated by the Protobuf compiler) such as google.protobuf.Timestamp
.
I’m writing a library with an utility class that can convert such classes. For example:
fun Timestamp.toInstant() = Instant.ofEpochSecond(this.seconds, this.nanos.toLong())
In order to compile my library, I need to have these message classes (such as google.protobuf.Timestamp
) present (generated) in my project. However, I don’t want to distribute google.protobuf.Timestamp
in the library artifact, since that can cause version conflicts (because these projects generates server/client stubs together with such message classes, and they’re all designed to fit together – their internal API changes often so things will break if some files are overridden by dependency).