I'm learning Kotlin, current using Fedora 25 OpenJDK 8 and Kotlin 1.1.
I copied the example from the Kotlin website: https://kotlinlang.org/docs/reference/delegated-properties.html and changed the get operator.
class Example {
var p: String by Delegate()
}
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
// My implementation
return property.getter.call(thisRef) as String
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name} in $thisRef.'")
}
}
Reading the Reflection documentation the getter expects the object instance and no other parameter, but I only achieved the following error.
(Error is abbreviate because it's too big, it's in recursion.)
.
.
.
at info.malk.Example.getP(Delegation.kt)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at kotlin.reflect.jvm.internal.FunctionCaller$Method.callMethod(FunctionCaller.kt:98)
at kotlin.reflect.jvm.internal.FunctionCaller$InstanceMethod.call(FunctionCaller.kt:115)
at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:107)
at info.malk.Delegate.getValue(Delegation.kt:32)
at info.malk.Example.getP(Delegation.kt)
.
.
.
Caused by: java.lang.reflect.InvocationTargetException
... 1024 more
Caused by: java.lang.StackOverflowError
... 1024 more
Process finished with exit code 1
Help.
Translation Rule says:
For instance, for the property prop the hidden property prop$delegate is generated, and the code of the accessors(getter/setter) simply delegates to this additional property.
so the kotlin property will dispatch the getter/setter to the delegator. when you get/set the value on a property around in the delegate handlers (getValue/setValue) will result in recursive call.
your Delegate should more like this:
class Delegate<T> {
private var value: T? = null;
// ^--- store the proeprty value internal
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value ?: throw UninitializedPropertyAccessException();
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = value;
}
}
Related
First of all, I'm using Kotlin in my JavaFX Project.
I'm trying to implement my own JavaFX Node that extends Canvas.
class BrikkCanvas(width: Double, height: Double, private val rows: Int, private val cols: Int) : Canvas(width, height)
I also want to be able to add BrikkCanvas directly in the FXML File like so
<BrikkCanvas fx:id="myCanvas" width="100.0" height="100.0" rows="1" cols="1" />
My class has no default constructor, that's why including it in FXML is not trivial.
I found out, however, that you can implement custom BuilderFactory, so I did:
class BrikkCanvasBuilderFactory : BuilderFactory {
private val defaultBuilderFactory = JavaFXBuilderFactory()
override fun getBuilder(clazz: Class<*>): Builder<*> =
if (clazz == BrikkCanvas::class.java) BrikkCanvasBuilder()
else defaultBuilderFactory.getBuilder(clazz)
private class BrikkCanvasBuilder : Builder<BrikkCanvas> {
var width: Double = 0.0
var height: Double = 0.0
var rows: Int = 0
var cols: Int = 0
override fun build(): BrikkCanvas = BrikkCanvas(width, height, rows, cols)
}
}
In the App class that extends Application I use my BrikkCanvasBuilderFactory like this:
fun startTheGame(playerName: String) {
val loader = FXMLLoader(App::class.java.getResource("game.fxml"), null, BrikkCanvasBuilderFactory())
val scene = Scene(loader.load())
val controller = loader.getController<GameController>()
primaryStage.scene = scene
}
However, when I start the application and click the button that invokes startTheGame, I get the following error:
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
...
Caused by: java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
...
Caused by: javafx.fxml.LoadException:
/path to project/target/classes/game.fxml:21
at App$Companion.startTheGame(App.kt:18)
...
Caused by: java.lang.IllegalStateException: defaultBuilderFactory.getBuilder(clazz) must not be null
at controller.BrikkCanvasBuilderFactory.getBuilder(BrikkCanvasBuilderFactory.kt:12)
game.fxml:21 = fx:controller="controller.GameController"
App$Companion.startTheGame(App.kt:18) = val scene = Scene(loader.load())
BrikkCanvasBuilderFactory.kt:12 = else defaultBuilderFactory.getBuilder(clazz)
Please note that I haven't even included any <BrikkCanvas ... /> tags in the FXML File yet
I'm pretty sure that I'm missing something really basic that probably has to do with the fact that I'm using Kotlin instead of Java
As I understand it, the BuilderFactory and Builder interfaces are relics of the past (essentially deprecated) and should be avoided in new code—pretend they don't exist. You should instead annotate your constructor parameters with the #NamedArg annotation.
class BrikkCanvas(
#NamedArg("width") width: Double,
#NamedArg("height") height: Double,
#param:NamedArg("rows") private val rows: Int,
#param:NamedArg("cols") private val cols: Int
) : Canvas(width, height)
The FXMLLoader will then be able to tell which FXML attribute corresponds with which parameter, and thus can set the properties via the constructor.
With Bean Validation 2.0 it is possible to also put constraints on container elements.
I cannot get this to work with Kotlin data classes:
data class Some(val someMap: Map<String, #Length(max = 255) String>)
This does not have any effect. Any ideas?
I created a repository with a sample project to reproduce the case: https://github.com/mduesterhoeft/bean-validation-container-constraints
Add this config to your build.gradle (note that ... means whatever is already there) :
Groovy:
compileKotlin {
kotlinOptions {
freeCompilerArgs = [..., "-Xemit-jvm-type-annotations"]
...
}
}
Kotlin DSL:
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf(..., "-Xemit-jvm-type-annotations")
...
}
}
Starting Kotlin 1.3.70 and 1.4, this should be possible setting a specific compiler option: https://kotlinlang.org/docs/reference/whatsnew14.html#type-annotations-in-the-jvm-bytecode .
On any previous version or any situation where this support is not sufficient, you have to write a custom validator.
Example one for validating that a collection only contains hex strings:
#Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.FIELD,
AnnotationTarget.ANNOTATION_CLASS,
AnnotationTarget.CONSTRUCTOR,
AnnotationTarget.VALUE_PARAMETER
)
#Retention(AnnotationRetention.RUNTIME)
#MustBeDocumented
#Constraint(validatedBy = [HexStringElementsValidator::class])
annotation class HexStringElements(
val message: String = "must only contain hex values",
val groups: Array<KClass<*>> = [],
val payload: Array<KClass<out Any>> = []
)
class HexStringElementsValidator : ConstraintValidator<HexStringElements, Collection<Any>> {
companion object {
val pattern = "^[a-fA-F0-9]+\$".toRegex()
}
override fun isValid(value: Collection<Any>?, context: ConstraintValidatorContext?) =
value == null || value.all { it is String && pattern.matches(it) }
}
I'm porting a class from Java to Kotlin. This class declares hundreds of objects. Each object has a name property which is identical with the declared variable name of the object. Java reflection allows to use the declared name via reflection to set the object member name. Just saves one parameter in hundreds of constructors.
I try to do the same in Kotlin but can't figure out how to do the property setting. Here is some simplified test code:
import kotlin.reflect.full.companionObject
import kotlin.reflect.full.declaredMemberProperties
class MyTestObject() {
var name: String = "NotInitialized"
companion object {
val Anton = MyTestObject()
val Berta = MyTestObject()
val Caesar = MyTestObject()
}
}
fun main(args : Array<String>) {
println(MyTestObject.Anton.name) // name not yet initialized
// Initialize 'name' with the variable name of the object:
for (member in MyTestObject::class.companionObject!!.declaredMemberProperties) {
if (member.returnType.toString() == "myPackage.MyTestObject") {
println("$member: ${member.name}")
// Set 'name' property to 'member.name':
// ???
}
}
println(MyTestObject.Anton.name) // now with the initialized name
}
The ??? line is where I would like to get access to the name property of MyTestObject to set it to to member.name. I'm looking for a function similar to (member.toObject() as MyTestObject).name = member.name.
While kotlin-reflection strives to be type-safe, sometimes the type system and the inference logic are not enough to allow for the things like what you are trying to do in a type-safe way. So, you have to make unchecked casts, stating that your knowledge about the types is more than the compiler can infer.
In your case, it's enough to cast member so that you can pass the companion object instance into its .get(...) and use the result as a MyTestObject, replace the // ??? line with:
#Suppress("UNCHECKED_CAST")
(member as KProperty1<Any, MyTestObject>)
.get(MyTestObject::class.companionObject!!.objectInstance!!)
.name = member.name
If you can replace MyTestObject::class.companionObject!! with MyTestObject.Companion::class (i.e. your actual use case does not involve getting .companionObject from different classes), the unchecked cast is not needed, and you can replace the statement above with this:
(member.get(MyTestObject.Companion) as MyTestObject).name = member.name
As an alternative that does not require companion object reflection at all, you can do the same binding logic with the delegation. Implementing provideDelegate allows you to customize the logic of initializing the property, and that's where you can assign the names:
operator fun MyTestObject.provideDelegate(
thisRef: MyTestObject.Companion,
property: KProperty<*>
) = apply { name = property.name }
operator fun MyTestObject.getValue(
thisRef: MyTestObject.Companion,
property: KProperty<*>
) = this
Then declare your properties as
val Anton by MyTestObject()
val Berta by MyTestObject()
val Caesar by MyTestObject()
Here is the final test code based on hotkey's solution:
package myPackage
import kotlin.reflect.full.declaredMemberProperties
class MyTestObject() {
lateinit var name: String
companion object {
val Anton = MyTestObject()
val Berta = MyTestObject()
val Caesar = MyTestObject()
init {
for (member in MyTestObject.Companion::class.declaredMemberProperties) {
if (member.returnType.toString() == "myPackage.MyTestObject") {
(member.get(MyTestObject.Companion) as MyTestObject).name = member.name
}
}
}
}
}
fun main(args : Array<String>) {
println(MyTestObject.Anton.name)
println(MyTestObject.Caesar.name)
}
So I have hundreds of fields in a couple of classes and I'd like to write some methods on them where they automatically println each field and its corresponding value
At the moment I have this:
inner class Version(val profile: Profile) {
#JvmField val MINOR_VERSION = glGetInteger(GL_MINOR_VERSION)
fun write(file: File? = null) {
//file.printWriter().use { out -> out.pri }
this::class.java.fields.forEach {
println(it.isAccessible)
println(it.getInt(it)) }
}
}
But this is what I get:
false
Exception in thread "main" java.lang.IllegalArgumentException: Can not set final int field uno.caps.Caps$Version.MINOR_VERSION to java.lang.reflect.Field
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
at sun.reflect.UnsafeQualifiedIntegerFieldAccessorImpl.getInt(UnsafeQualifiedIntegerFieldAccessorImpl.java:58)
Any idea?
Instead of using Java fields and Java reflection code, you can also use Kotlin properties and Kotlin reflection classes:
class Reflector {
val Foo = 1;
fun printFields() {
this::class.memberProperties.forEach {
if (it.visibility == KVisibility.PUBLIC) {
println(it.name)
println(it.getter.call(this))
}
}
}
}
It seems that you are passing the Field variable it as a parameter getInt whereas the parameter should be the object the field belongs to this:
From the Javadoc for Field.getInt(Object obj):
obj - the object to extract the int value from
Perhaps this is what you meant to do:
class Reflector {
#JvmField val Foo = 1;
fun printFields() {
this.javaClass.fields.forEach {
println(it.isAccessible)
println(it.getInt(this))
}
}
}
fun main(args : Array<String>) {
Reflector().printFields()
}
I was trying to cast an unknown given object to the required class using kotlin primitives conversion methods like this:
private fun callSelfParser(origObj: Any, destClazz: KClass<*>): Any {
val methodName = when (destClazz) {
Int::class -> "toInt"
Char::class -> "toChar"
Long::class -> "toLong"
Byte::class -> "toByte"
Float::class -> "toFloat"
Short::class -> "toShort"
Double::class -> "toDouble"
Boolean::class -> "toBoolean"
else -> ""
}
val parsingMethod: KFunction<*>
try {
parsingMethod = origObj::class.functions.toList().first { it ->
it.name == methodName
}
} catch (e: NoSuchElementException) {
throw ObjectMapperEx("Element $origObj doesn't have a method for parsing to $destClazz")
}
return parsingMethod.call(origObj)!!
}
But I got the following exception when executing the .call():
Exception in thread "main" kotlin.reflect.jvm.internal.KotlinReflectionInternalError: Reflection on built-in Kotlin types is not yet fully supported. No metadata found for public open val length: kotlin.Int defined in kotlin.String[DeserializedPropertyDescriptor#1cbb87f3]
Can someone say whats wrong or, if there's another way to achieve my goal?