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.
I am new to kotlin and I am trying to use main function together with a class.
fun main() {
var demo = Person("Hello", 10)
println(demo)
}
private class Person (name: String, age: Int){
var name: String
var age: Int
init {
this.name = name
this.age = age
}
}
Although I have declared the main function, the compiler is still looking for the static main method in the class that I have defined, Person, and I got this error:
Error: Main method not found in class Person, please define the main method as:
public static void main(String[] args)
or a JavaFX application class must extend javafx.application.Application
What have I missed?
java Person is the problem, you are specifically telling it to look for the static void main method there. Top-level functions from Person.kt will be compiled into a PersonKt class by default and so you should run java PersonKt.
Also, the unnamed package you get by not specifying any package is weird in some ways, so I would recommend adding a package declaration from the start (and running java your.package.PersonKt).
I'm trying to follow this tutorial https://dev.to/tagmg/step-by-step-guide-to-building-web-api-with-kotlin-and-dropwizard and am instead writing my gradle.build file in Kotlin's DSL and am finding there is no direct mapping from Groovy to Kotlin and I'm now getting this error when running ./gradlew run:
(4, 1): Duplicate JVM class name 'dropwizard/tut/AppKt' generated from: package-fragment dropwizard.tut, package-fragment dropwizard.tut
plugins {
// Apply the Kotlin JVM plugin to add support for Kotlin on the JVM.
id("org.jetbrains.kotlin.jvm").version("1.3.31")
// Apply the application plugin to add support for building a CLI application.
application
}
repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here.
mavenCentral()
jcenter()
}
dependencies {
// Use the Kotlin JDK 8 standard library.
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// Use the Kotlin test library.
testImplementation("org.jetbrains.kotlin:kotlin-test")
// Use the Kotlin JUnit integration.
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
compile("io.dropwizard:dropwizard-core:1.3.14")
}
application {
// Define the main class for the application
mainClassName = "dropwizard.tut.AppKt"
}
tasks.withType<Jar> {
manifest {
attributes["Main-Class"] = application.mainClassName
}
from({
configurations.runtimeClasspath.get().filter { it.name.endsWith("jar") }.map { zipTree(it) }
})
}
tasks.named<JavaExec>("run") {
args("server", "config/local.yaml")
}
I cannot tell (yet) why this happens but to work around it add #file:JvmName("SomethingUnique") to your JVM file. Note that renaming the file will not help and lead to the same error. Only changing the output name will resolve it.
The JVM only knows how to load classes, so the Kotlin-to-JVM compiler generates classes to hold top-level val or fun declarations.
When you have two similarly named files
// src/commonMain/kotlin/com/example/Foo.kt
package com.example
val a = 1
and
// src/jvmMain/kotlin/com/example/Foo.kt
package com.example
val b = 2
the kotlin-to-JVM compiler generates
package com.example;
public class FooKt {
public static final int a = 1;
}
and
public com.example;
public class FooKt {
public static final int b = 2;
}
Obviously, these two files can't coexist in the same JVM ClassLoader, hence the error message.
Solutions involve:
As #Fleshgrinder noted, adding a file-level JvmName annotation to at least one to override the derived name, FooKt.
Renaming files to be different where possible.
Moving top-level val and fun declarations from those files into other files so Kotlin does not need to create the FooKt class.
Moving top-level val and fun declarations into objects or companion objects.
I try to run Kotlin instrumentation tests for android.
In my app/build.gradle:
android {
dataBinding {
enabled = true
}
compileSdkVersion 28
defaultConfig {
applicationId "com.myproject"
minSdkVersion 18
targetSdkVersion 28
versionCode 6
versionName "0.0.7"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
androidTest.java.srcDirs += 'src/androidTest/kotlin'
}
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-alpha02'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test:rules:1.1.2-alpha02'
androidTestImplementation 'androidx.test:runner:1.1.2-alpha02'
In folder /app/src/androidTest/kotlin/com/myproject/ I has Kotlin test:
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import org.junit.Rule
import org.junit.runner.RunWith
import androidx.test.rule.ActivityTestRule
import com.myproject.ui.activity.TradersActivity
import org.junit.Before
#RunWith(AndroidJUnit4::class)
#SmallTest
class TradersActivityTest {
private lateinit var stringToBetyped: String
#get:Rule
var activityRule: ActivityTestRule<TradersActivity> = ActivityTestRule(TradersActivity::class.java)
#Before
fun initValidString() {
// Specify a valid string.
stringToBetyped = "Espresso"
}
}
but when I run test I get error:
$ adb shell am instrument -w -r -e debug false -e class 'com.myproject.TradersActivityTest' com.myproject.debug.test/androidx.test.runner.AndroidJUnitRunner
Client not ready yet..
Started running tests
java.lang.RuntimeException: Delegate runner 'androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner' for AndroidJUnit4 could not be loaded.
at androidx.test.ext.junit.runners.AndroidJUnit4.throwInitializationError(AndroidJUnit4.java:92)
at androidx.test.ext.junit.runners.AndroidJUnit4.loadRunner(AndroidJUnit4.java:82)
at androidx.test.ext.junit.runners.AndroidJUnit4.loadRunner(AndroidJUnit4.java:51)
at androidx.test.ext.junit.runners.AndroidJUnit4.<init>(AndroidJUnit4.java:46)
at java.lang.reflect.Constructor.newInstance(Native Method)
at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
at androidx.test.internal.runner.junit4.AndroidAnnotatedBuilder.runnerForClass(AndroidAnnotatedBuilder.java:63)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
at androidx.test.internal.runner.AndroidRunnerBuilder.runnerForClass(AndroidRunnerBuilder.java:153)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at androidx.test.internal.runner.TestLoader.doCreateRunner(TestLoader.java:73)
at androidx.test.internal.runner.TestLoader.getRunnersFor(TestLoader.java:104)
at androidx.test.internal.runner.TestRequestBuilder.build(TestRequestBuilder.java:789)
at androidx.test.runner.AndroidJUnitRunner.buildRequest(AndroidJUnitRunner.java:544)
at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:387)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1879)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance(Native Method)
at androidx.test.ext.junit.runners.AndroidJUnit4.loadRunner(AndroidJUnit4.java:72)
... 16 more
Caused by: org.junit.runners.model.InitializationError
at org.junit.runners.ParentRunner.validate(ParentRunner.java:418)
at org.junit.runners.ParentRunner.<init>(ParentRunner.java:84)
at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:65)
at androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner.<init>(AndroidJUnit4ClassRunner.java:43)
at androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner.<init>(AndroidJUnit4ClassRunner.java:48)
... 18 more
Tests ran to completion.
I faced this problem in kotlin , used below code and it worked
#RunWith(AndroidJUnit4::class)
#LargeTest
class MainActivityTest {
private val TAG = "MainActivityTest"
#get:Rule
val activityRule = ActivityTestRule(MainActivity::class.java)
}
follow the link below:-
https://developer.android.com/training/testing/junit-rules
The error message displayed when I delete the #Test method.
You can try to put a #Test method and run
#Test
public void signInTest() {
}
For those who still have problems like this, This happens because Kotlin compiler wants to generate getter/setter properties. for example, I've got this error because I didn't mention #Rule annotation with #get:Rule.
If we annotate properties like this with #get:... or #JvmField it could solve the problem.
#get:Rule
var mActivityRule = ActivityTestRule(MainActivity::class.java)
Or
#Rule
#JvmField
var mActivityRule = ActivityTestRule(MainActivity::class.java)
#JvmField instructs the Kotlin compiler not to generate getters/setters for this property and expose it as a field.
In regards to #RunWith(AndroidJUnit4::class)
Delete
import androidx.test.ext.junit.runners.AndroidJUnit4
Use
import androidx.test.runner.AndroidJUnit4
I got this error when I have some parameters in my test method.
So, if you have it - delete it.
#Test
fun someTestMethod(someParams: Int) { // delete method params
assert(1==1)
}
just make sure that if your test is calling any suspend methods, you run your test with runBlocking{}. Solved the problem for me
Max Kilzieh answer worked for me
Using import androidx.test.runner.AndroidJUnit4 instead of import androidx.test.ext.junit.runners.AndroidJUnit4
I just wasted all day on this issue 😵
I'm guessing you are using the multiplatform plugin like I am.
I was about to give up on trying to moving my instrumented tests to src/androidTest/kotlin instead of src/androidTest/java
when I decided to log kotlin.sourcesets.names, which printed
[androidAndroidTest, androidAndroidTestDebug, androidDebug, androidMain, androidRelease, androidTest, androidTestDebug, androidTestRelease, commonMain, commonTest, iosArm32Main, iosArm32Test, iosArm64Main, iosArm64Test, iosX64Main, iosX64Test, macosX64Main, macosX64Test]
Soo I realized in order for dependencies to resolve correctly I needed to change
androidTestImplementation -> androidAndroidTestImplementation
testImplementation -> androidTestImplementation
They don't describe this in the documentation of course.
(as far as I have found)
... so for example, for you, I think the dependencies for your instrumented test dependencies should be:
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-alpha02'
not
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-alpha02'
You don't have any functions annotated with #Test. This is likely the cause of the issue. I've run my own test using the AndroidJUnit4 runner on a class without #Test functions, and reconstructed this behavior.
The AndroidJUnit4 JUnit4 runner is the cause of this issue, and despite its attempt to provide a detailed message (see the code where the exception is thrown), it gives you a wrong message.
The following are more ways to achieve this error:
#Test
fun willThrowInitializationErrorBecauseReturnsNonUnitValue(): Int {
return 0
}
#Test
fun willThrowInitializationErrorBecauseTakesParameters(param: Int) {
}
The error is generated by the fact that you're writing JUnit4 style tests, and they're expected to conform to JUnit4 semantics, but it would be nice if the error message were more clear.
To make matters worse, if one test function is messed up the runner won't run any tests because the InitializationError is thrown in the constructor of the AndroidJUnit4 class.
In My case while using Java,I faced issue when I did below mistakes.
1)I had made ActivityTestRule as private.
All test cases should be public always.
2)I didn't add static for BeforeClass and AfterClass methods
Methods of #BeforeClass and #AfterClass should be static
Ex)#BeforeClass
public static void setUp(){
}
Also just to expand on the answers, but it won't help for this particular reason:
If you use kotlin and get this while testing, make sure you aren't using suspend in DAO.
(Sorry to put it here, there was no thread with this in kotlin and I already found the solution so it might help someone)
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