Kotlin class won't let me extend abstract class from different file - kotlin

I am working on an object-oriented project, and I want to improve my understanding of the OOP part of Kotlin. I have the following abstract class:
package Objecttest
abstract class Abstractclasstest {
abstract fun testString(s: String): String
}
Now I want to extend it in a new class in a different file like this:
package Objecttest
public class Newclasstest : Abstractclasstest() {
override fun testString(s: String): String {
return s
}
}
but when I try compiling Newclasstest.kt, I am met with the following error message: "error: unresolved reference: Abstractclasstest".
Folder structure:
Objecttest/
├── Abstractclasstest.kt
├── Newclasstest.kt
Why is this and how can I work around it? The most important bit is why, because I want to avoid the same mistake in the future.

It seems to me that you need to compile Abstractclasstest.kt first using kotlinc Abstractclasstest.kt and only then compile Newclasstest.kt as follows: kotlinc -cp . Newclasstest.kt. This will search for class files in the same path as Newclasstest.kt on which it should already find the one corresponding to Abstractclasstest.kt. Or you can just compile the 2 files at the same time using kotlinc *.kt.

Related

How do you set up a property in a custom gradle task?

I want to write a task that takes a directory from , does something with the files in it and writes the result into some other directory to.
I've been led to believe this was the way to define such a task (kotlin dsl):
package my.app
abstract class FooBarTask : DefaultTask() {
#get:InputDirectory
abstract val from: Property<Directory>
#get:OutputDirectory
abstract val to: Property<Directory>
#TaskAction
fun doSomething() {
println("Hakuna Matata")
}
}
now how do I set the from and to value in a groovy-based build.gradle?
def myTask = tasks.register('myTask', FooBarTask) {
from = layout.projectDirectory.dir("foo")
to = layout.buildDirectory.dir("bar")
}
this results in
Could not create task ':my-subproject:myTask'.
> Please use the ObjectFactory.directoryProperty() method to create a property of type Directory.
and it shouldn't.
How do you correctly define a directory property in a custom task?
Gradle has the specialized DirectoryProperty, that offers some additional functionality, compared to the plain Property<Directory> which is one of the implemented interfaces. So this specialized type should be used when declaring directory inputs/outputs.
I'm actually not a 100% sure what caused the error you saw.

Kotlin compile error "unresolved reference" only when compiling from command line

I have the Kotlin code:
import com.google.gson.Gson
import java.io.File
class System {
/*
Save current game state to file
*/
internal fun saveGameState(game: Game) {
val gameState = game.getGameState()
val gson = Gson()
val jsonString = gson.toJson(gameState)
val pathname = FILENAME_SAVED_GAME
File(pathname).writeText(jsonString)
}
private fun loadGameState(): Game {
val jsonString = File(FILENAME_SAVED_GAME).readText(Charsets.UTF_8)
val gson = Gson()
val gameState: GameState = gson.fromJson(jsonString, GameState::class.java)
File(FILENAME_SAVED_GAME).delete()
return Game(gameState)
}
...
}
When I run it from within IntelliJIDEA, it compiles and runs fine, including using this class to save state and restore state.
When I compile it from the command line, I use this command:
kotlinc *.kt -include-runtime -d GizmosK.jar
and I this error message:
System.kt:1:12: error: unresolved reference: google
import com.google.gson.Gson
^
System.kt:11:20: error: unresolved reference: Gson
val gson = Gson()
^
System.kt:19:20: error: unresolved reference: Gson
val gson = Gson()
^
I've successfully used this command many times before, but possibly not since adding this System class, which is the only one that imports anything from outside my project.
I've searched, and found this question which appears very similar to my problem, but I have a few concerns.
First, I don't know if this solution applies to my situation since my code is pure Kotlin and not Android.
Second, I don't know how to generalize their solution and make it apply to my situation. Like, I know I probably have to replace something with google.gson or com.google.gson or com.google.gson.Gson in there somewhere, but I don't know where, since there are three things there that look like package names. Do I need all three? I also don't know if expressions like package_name are literal strings I should enter verbatim or if I should replace those words with the actual package name.
Third, I've never had to specify flovar/flavor nor resource_package before in a command line, and I don't want to introduce new variables, if at all possible.
BTW, I'm compiling from the command line to generate a .jar file to distribute so anyone can run it from the command line without sharing my code or requiring they install IntelliJ IDEA.

How do I add new type-safe accessors to a Gradle Project?

We have a plugin which defines additional properties and adds them as extension properties, like:
project.extra["copyright"] = "Copyright ..."
Then in the build scripts, I can access this like:
project.extra["copyright"]
I'd like to just write:
project.copyright
Some Gradle plugins seem to do something like this. I can access project.sourceSets or project.kotlin even though those certainly aren't in the Project interface.
Using an IDE, I can drill into those convenience methods, which then lands me in some autogenerated code, so I know it's being autogenerated somewhere, but I haven't been able to find any clues to how to get this to happen for our own plugin. The Gradle docs mention type-safe accessors which is ultimately what allowed me to phrase the question, but the docs don't say how to add new ones.
How do we get this treatment for our own plugin?
Creating DSL-like APIS is documented here: https://docs.gradle.org/current/userguide/implementing_gradle_plugins.html#modeling_dsl_like_apis.
It's pretty easy to do. Here's a quick guide, and some tips.
Define an extension.
import org.gradle.api.provider.Property
interface MyExtension {
val copyright: Property<String>
}
It looks pretty boring! What's important is that it's either be an abstract class, or an interface - this is so Gradle can create a new instance (see 'Managed types'), and this is where the Gradle magic begins.
Aside: I've used Property<String> instead of String, although both will work. I recommend using types compatible with Lazy Configuration.
Register the extension.
import org.gradle.api.*
abstract class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
val myExtension: MyExtension =
project.extensions.create("myPlugin", MyExtension::class.java)
}
}
This is the magic part. Simply by registering the extension against the project, Gradle will make the extension available and automatically generate Kotlin-DSL convenience methods.
Apply the plugin
// build.gradle.kts
plugins {
id("my-plugin")
}
myPlugin {
copyright.set("blah blah 2022")
}
Just like that, Gradle will generate Kotlin DSL accessors. Here's one of them:
// Accessorsajp3oxzka99ro52ctxwv0petb.kt
/**
* Configures the [myPlugin][MyExtension] extension.
*/
fun org.gradle.api.Project.`myPlugin`(configure: Action<MyExtension>): Unit =
(this as org.gradle.api.plugins.ExtensionAware).extensions.configure("myPlugin", configure)
Use the extension values
Going back to the Plugin definition, lets say you want to register your own task:
// a demo task
abstract class MyTask : DefaultTask() {
#get:Input
abstract val copyright: Property<String>
#TaskAction
fun run() {
println("Copyright is ${copyright.get()}")
}
}
(Note that this task, like the extension, is a 'managed type').
Now the custom task can be registered, and a default value for copyright set.
abstract class MyPlugin : Plugin<Project> {
override fun apply(project: Project) {
val myExtension: MyExtension = project.extensions.create("myPlugin", MyExtension::class.java)
project.tasks.register("myCustomTask", MyTask::class.java) {
copyright.set(myExtension.copyright)
}
}
}
It's good that both MyExtension and MyTask use Property<String> - the actual value will be evaluated lazily, and only if required.
Now if I run ./gradlew :myCustomTask, I see:
> Task :myCustomTask
Copyright is blah blah 2022
Further reading
What if you want to have multiple copyrights? Then you can create a configuration container
What if MyExtension has lots of properties and you want to provide them all to MyTask? Then you can use #Nested inputs
The extension properties can have default values.

How to bind i18next-browser-languagedetector to Kotlin?

The Gradle project is set by the JS plugin:
plugins {
kotlin("js") version("1.6.10")
}
and uses the LEGACY compilation backend:
kotlin {
js(LEGACY) {
// ...
}
}
My goal is to use the following dependencies in Kotlin sources:
dependencies {
implementation(npm("i18next", "21.6.11"))
implementation(npm("react-i18next", "11.15.4"))
implementation(npm("i18next-browser-languagedetector", "6.1.3"))
}
It was pretty easy to describe JS-Kotlin bridging for the first two dependencies:
#JsModule("i18next")
#JsNonModule
external val i18next: I18n
external interface I18n {
fun use(module: dynamic): I18n
}
#JsModule("react-i18next")
#JsNonModule
external val reactI18next: ReactI18next
external interface ReactI18next {
val initReactI18next: dynamic
}
Unfortunately, the last one - i18next-browser-languagedetector - is driving me some nuts with its configuration. Something like this:
#JsModule("i18next-browser-languagedetector")
#JsNonModule
external val LanguageDetector: dynamic
doesn't work - the actual LanguageDetector provided by the declaration above is {}, so i18next doesn't consume it in Kotlin code (the JS code throws You are passing a wrong module! Please check the object you are passing to i18next.use()):
i18next.use(LanguageDetector) // fails
Can anyone please help me with a declaration of a JS-Kotlin bridge for the LanguageDetector?
Well, by debugging a little bit I've managed to solve this JS-Kotlin bridging issue. The working solution is the following declaration:
#JsModule("i18next-browser-languagedetector")
#JsNonModule
external val i18nextBrowserLanguageDetector: I18nextBrowserLanguageDetector
external interface I18nextBrowserLanguageDetector {
#JsName("default")
val LanguageDetector: dynamic
}
Now it's possible to do first parts of the i18next initialization chain:
i18next
.use(i18nextBrowserLanguageDetector.LanguageDetector)
.use(reactI18next.initReactI18next)
// ...
Unfortunately, it's difficult to say that I'm getting any intuition behind it (maybe because of my huge blind spots in JS) - so any additional clarification or explanations would be helpful still.
My biggest concern is that LanguageDetector from the declaration above should be a class, but it seems like no way to use something else rather than dynamic property. When I try to lift up the #JsName("default") annotation to mark some class protocol with it, it doesn't compile:
#JsModule("i18next-browser-languagedetector")
#JsNonModule
#JsName("default")
external class LanguageDetector
It's not possible to use a nested class inside of the interface as well in this case:
#JsModule("i18next-browser-languagedetector")
#JsNonModule
external interface I18nextBrowserLanguageDetector {
#JsName("default")
class LanguageDetector
}
So while it seems to be solved, it's super-frustrating still.

Kotlin Multiplatform Expected Annotations Returns `This class does not have a constructor` with neo4j actual typealias

I have a multiplatform project with a modal class, User.
User.kt
class User {
val id = -1
val username = ""
val age = -1
val nickname = ""
}
I also have expected and actual annotations
Annotation.kt [Common Module]
expect annotation class NodeEntity
expect annotation class Id
expect annotation class GeneratedValue
More over, I have their actual implementation
Annotation.kt [JVM Module]
actual typealias ValueFor = org.neo4j.ogm.annotation.ValueFor
actual typealias NodeEntity = org.neo4j.ogm.annotation.NodeEntity
actual typealias Id = org.neo4j.ogm.annotation.Id
actual typealias GeneratedValue = org.neo4j.ogm.annotation.GeneratedValue
actual typealias Relationship = org.neo4j.ogm.annotation.Relationship
I then went back and annotated my User.kt
#NodeEntity
class User {
#Id
#GeneratedValue
val id = -1
val username = ""
val age = -1
val nickname = ""
}
But When I compile it, I get this error
Task :compileKotlinJvm FAILED
e: ...User.kt: (13, 2): This class does not have a constructor
e: ...User.kt: (21, 6): This class does not have a constructor
e: ...User.kt: (22, 6): This class does not have a constructor
What Am I doing wrong?
N:B. Attempts done
Made the expected annotations have a constructor [no success]
Made the expected annotations match with a constructor [ERROR: Parameter ''{0}'' has conflicting values in the expected and actual annotation]
FYI: My build.gradle already has the noArg in place, so that the User.kt class is compiled with a no argument public constructor
Your expect annotations probably need explicit parenthesis.
expect annotation class SharedImmutable()
actual typealias SharedImmutable = kotlin.native.SharedImmutable
https://github.com/touchlab/Stately/blob/4b17057ad5d55f51f4ccf971cf79e51585ad2324/src/commonMain/kotlin/co/touchlab/stately/annotation/Annotations.kt#L26
I experienced a similar issue, but seemingly like the OP, I already included the explicit parenthesis. My particular issue had to do with the Java Library, in the Java Source Set, not being available to another Gradle Sub-project that was depending on it.
TL;DR
Assert that you are properly exposing the platform-specific dependencies. For instance, properly using implementation and api in the build.gradle files.
Elaborating on my scenario
I had a Gradle multi-project build:
Project
AppSubProject
LibrarySubProject
Where AppSubProject depended on LibrarySubProject. Both Gradle Sub-projects were Kotlin Multi-platform Modules.
In LibrarySubProject, there was an exposed Annotation Class:
Common Source Set:
expect annotation class Inject()
JVM Source Set:
actual typealias Inject = javax.inject.Inject
The Kotlin Common Inject annotation was available to AppSubProject since it depended on the LibrarySubProject.
AppSubProject/build.gradle:
...
commonMain {
dependencies {
implementation project(":LibrarySubProject")
...
The Cause of the Issue
In the LibrarySubProject/build.gradle file I wasn't exposing the JVM dependency:
...
jvmMain {
dependencies {
implementation "javax.inject:javax.inject:1"
...
As you can see, I was using implementation instead of api. So when I used the annotation on a constructor of a class in AppSubProject:
class Example #Inject constructor()
and when I built the AppSubProject, it couldn't resolve the JVM dependency, and was cryptically giving me the following error:
e: Example.kt: This class does not have a constructor
The Solution
The solution was simply to expose the JVM dependency so that it could be resolved in the other module. So changing the implementation to api resolved the issue.
...
jvmMain {
dependencies {
api "javax.inject:javax.inject:1"
...
Summation
If you experience this issue, assert the following:
The Kotlin Common Annotation Class explicitly provides the constructor parenthesis as noted in this answer
All necessary platform-specific dependencies are exposed properly