3

I instantiate my object using:

val currency = Currency::class.createInstance()
            .copy(code = "GBP")

but I'm getting an exception at that line:

java.lang.IllegalArgumentException: Class should have a single no-arg constructor: class com.abc.Currency
    at kotlin.reflect.full.KClasses.createInstance(KClasses.kt:281)
    at com.abc.MyInteractorTest.setUp(MyInteractorTest.kt:55)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:79)
    at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:85)
    at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
    at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:106)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
    at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:66)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
    at sun.reflect.GeneratedMethodAccessor35.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
    at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:117)
    at sun.reflect.GeneratedMethodAccessor34.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:155)
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:137)
    at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
    at java.lang.Thread.run(Thread.java:748)
ericn
  • 12,476
  • 16
  • 84
  • 127
  • 2
    So does the Currency class has a no-argument constructor? – yole Dec 13 '18 at 14:02
  • What does class `Currency` look like? Is this class `java.util.Currency` or a class that you wrote yourself, or something else? – Jesper Dec 13 '18 at 14:28
  • ... as there is a `copy`-call I assume it is a `data class`... and also I don't know of any `Currency`-class that supports `copy` (or which would want to)... nonetheless, I am also interested in that class and also why that `copy`-call is even used... – Roland Dec 13 '18 at 14:37

3 Answers3

6

Either ensure that your data class initializes all values (therefore a default constructor will be available) or supply an appropriate default constructor yourself:

// setting default values on all properties:
data class Currency(val code : String = "GBP" / * all other properties need to have a default value assigned too */)

// or add a default constructor:
data class Currency(val code : String /* other properties */) {
  constructor() : this("GBP" /* other properties default values */)
}

Note that createInstance uses singleOrNull internally and throws an exception if there exists more than 1 (or none) constructor meeting the criteria of having just optional parameters. So if you have several such constructors or you do not want to change the Currency-class, then iterate over the available constructors, use the one that fits and fill up the parameters instead. You can then probably even skip that copy-call.

Why do you even use this approach to copy the data? copy only makes sense if there is already an object where you only want to change some of the properties but otherwise keep the rest as is and your sample could probably even be written as just:

val payInCurrency = Currency(code = "GBP")

You already know that you want a Currency and you also know that you want the code to be "GBP".

Roland
  • 22,259
  • 4
  • 57
  • 84
4

You're calling createInstance(), and the documentation for that method says:

Creates a new instance of the class, calling a constructor which either has no parameters or all parameters of which are optional (see KParameter.isOptional). If there are no or many such constructors, an exception is thrown.

You're calling a createInstance on class and it's throwing the exception specified, the class either:

  • has no constructor with no parameters (or where all parameters are optional)
  • has multiple constructors meeting that definition
Geoffrey Wiseman
  • 5,459
  • 3
  • 34
  • 52
1

Sorry for the confusion and thanks for the help.
Indeed, it's obvious that KClasses.createInstance() requires classes to have a "no-arg constructor".

So 1 solution is to change my classes to introduce a "no-arg constructor".

Another solution is to use my own createInstance() which accepts classes without "no-arg constructor". Turned out my team already have our own createInstance() and I was using the wrong method (KClasses.createInstance())

ericn
  • 12,476
  • 16
  • 84
  • 127
  • Hey, could you provide an example of your own createInstance implementation? would be super interested in it. i'm currently trying to create an instance of a generic and as mentioned in the other answers it is failing if not all parameters are optional – Christian Dräger Mar 29 '20 at 09:05