Kotlin Class 应该有一个无参数的构造函数

Kotlin Class should have a single no-arg constructor

我实例化我的对象使用:

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

但我在该行遇到异常:

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.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.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access[=12=]0(ParentRunner.java:58)
    at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.mockito.internal.runners.DefaultInternalRunner.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.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)

要么确保您的data class initializes all values (therefore a default constructor will be available) or supply an appropriate default constructor自己:

// 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 */)
}

注意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.

为什么还要使用这种方法来复制数据? copy 只有在已经存在一个对象时才有意义,在该对象中您只想更改某些属性,但其余部分保持原样,您的示例甚至可能写成:

val payInCurrency = Currency(code = "GBP")

您已经知道您想要 Currency 并且您还知道您希望代码为 "GBP"

您正在调用 createInstance(),该方法的 documentation 表示:

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.

您在 class 上调用 createInstance,它抛出指定的异常,class 要么:

  • 没有不带参数的构造函数(或所有参数都是可选的)
  • 有多个构造函数满足该定义

抱歉造成混淆,感谢您的帮助。
事实上,很明显 KClasses.createInstance() 需要 类 才能有一个 "no-arg constructor"。

所以 1 个解决方案是更改我的 类 以引入 "no-arg constructor"。

另一种解决方案是使用我自己的 createInstance(),它接受 类 而没有 "no-arg constructor"。结果我的团队已经有了自己的 createInstance() 而我使用了错误的方法 (KClasses.createInstance())