Consider the following example
data class Foo(val value: Int) {
companion object {
#JvmStatic
val bar = "bar"
#JvmStatic
fun baz() = Unit
}
}
I want to obtain the static members of the Foo class...
Foo::class.staticProperties // []
Foo::class.staticFunctions // []
Why are the resulting collections empty, when the property and function have been annotated #JvmStatic?
It seems staticProperties and staticFunctions only work for Java classes.
The staticProperties documentation says:
Returns static properties declared in this class. Only properties representing static fields of Java classes are considered static.
Another issue in your case is that you're using Foo::class when you should be using Foo.Companion::class. They are not the same, accessing companion members using the class name is only syntactic sugar.
I found the following to work for your case:
val staticProperties = Foo.Companion::class::memberProperties.get().filter { it.hasAnnotation<JvmStatic>() }
val staticFunctions = Foo.Companion::class::functions.get().filter { it.hasAnnotation<JvmStatic>() }
Related
I have multiple data classes and each class has a corresponding class containing more info. I want to write a function in which I should be able to pass an identifier (table name corresponding to the data class). Based on this identifier, object of the corresponding class should be made, the value changed and this object should be returned as output of the function. I have written a simplified version of it on playground but I am unable to get it to work. Any help is appreciated.
class someClass(
)
class objectForSomeClass(
var value: String
)
class someOtherClass(
)
class objectForSomeOtherClass(
var value: String
)
class doSomething() {
companion object {
val classMap = mapOf(
"someClass" to objectForSomeClass::class,
"someOtherClass" to objectForSomeOtherClass::class,
)
}
// Create a map of class name to a new object based on the class name input
fun dummyFun(className: String, valueInput: String): Map<String, kotlin.Any> {
var returnObject = mutableListOf<Pair<String, kotlin.Any>>()
when(className) {
"SOME_CLASS" -> {
returnObject = mutableListOf<Pair<String, justDoIt.classMap["someClass"]()>>()
}
"SOME_OTHER_CLASS" -> {
returnObject = Map<String, justDoIt.classMap["someOtherClass"]()>
}
}
returnObject[className].value = valueInput
return returnObject
}
}
fun main() {
var obj = doSomething()
var t = obj.dummyFun("SOME_CLASS", "Value to be inserted")
// do something with t
}
Not knowing more about your classes (the ones in your code are not data classes – a data class in Kotlin is a specific type of class) I still think a lot could be simplified down to maybe even this:
fun createObject(className: String, value: String): Any? {
return when (className) {
"SomeClass" -> ObjectForSomeClass(value)
"SomeOtherClass" -> ObjectForSomeOtherClass(value)
// ...
else -> null
}
}
Additionally:
The classMap is not necessary, you can hard-code the cases in the when clause as in my example. There is also no need for reflection, which you would need to create instances from SomeType::class.
With getting rid of classMap you also do not need the companion object holding it anymore, and then you are left with one function for creating instances of your classes, and this function does not have to be in a class. You might put it into a singleton class called object in Kotlin (https://kotlinlang.org/docs/object-declarations.html#object-expressions)
Data classes in Kotlin: https://kotlinlang.org/docs/data-classes.html
You could maybe also replace each class someClass & class objectForSomeClass pair with a class someClass with a companion object.
I'm trying this:
class Foo<T> {
var member: T = T()
}
...but the Kotlin compiler gives me an error: Type parameter T cannot be called as function.
How do I default-construct a generic member variable?
Well, to access the type information, we need to use the reified keyword on the type, but this is only applicable in inlined functions. So instead of relying on direct construction, a workaround can be to use a generator function wrapped in the companion object that immediately sets the member right after construction
// Test class to verify the implementation
class Yolo {
override fun toString() = "Yolo swag"
}
class Foo<T : Any> {
lateinit var member: T
companion object {
inline fun <reified T : Any> newInstance() =
T::class.java.newInstance().let { memberInstance ->
Foo<T>().apply { member = memberInstance}
}
}
}
fun main() {
// generate a Foo<Yolo>
val foo = Foo.newInstance<Yolo>()
println(foo.member) // displays "Yolo swag"
}
It's implied that T has a public no-arg constructor, but in general case it may not be true. This code uses reflection to bypass compiler complains about it (which may end up with runtime error if you dissapoint the JVM expectations and indeed pass T without public no-arg constructor).
//Reified generics at class level are not yet supported in Kotlin (KT-33213),
// so you have to pass instance of `KClass` manually as a consructor parameter
class Foo<T : Any>(clazz: KClass<T>) {
var member: T = clazz.createInstance()
}
Consider the following code:
sealed class DataType<T : Any> {
abstract fun inputToType(input: String): T
abstract fun typeToSql(value: T): String
companion object {
val all = listOf(StringDt, LongDt)
}
}
object StringDt : DataType<String>() {
override fun inputToType(input: String) = input
override fun typeToSql(value: String) = "\"${value}\""
}
object LongDt : DataType<Long>() {
override fun inputToType(input: String) = input.toLong()
override fun typeToSql(value: Long) = value.toString()
}
val dataTypeList = listOfNotNull(StringDt, LongDt)
println(dataTypeList)
println(DataType.all)
Things to consider:
object as per documentation (and my understanding as well) is singleton and always instantiated
the two objects (StringDt and LongDt) are quite similar
The result of println(DataType.all) shows that one of the objects are not initialized. How is that possible? I would expect all the list elements to be initialized.
IntelliJ version: CE 2020.2
Kotlin plugin version: 1.4.0-release-IJ2020.2-1
Here's a running example which shows that the static list has a null element, while the non-static one contains both objects initialized.
It happens due to cyclical static initializations. It's pretty hard to explain this problem in two words but you can read about it here.
To fix this behavior you can change all initialization like this:
val all by lazy { listOf(StringDt, LongDt) }
Suppose I have many enum classes like the following:
enum class Hero(val alias: String) {
SUPERMAN("Clark Kent"),
BATMAN("Bruce Wayne");
companion object {
fun fromAlias(value: String): Hero? = Hero.values().find { it.alias.equals(value, true) }
}
}
enum class Villain(val alias: String) {
TWO_FACE("Harvey Dent"),
RIDDLER("Edward Nigma");
companion object {
fun fromAlias(value: String): Villain? = Villain.values().find { it.alias.equals(value, true) }
}
}
I'd like to be able to create a generic interface to handle the fromAlias method in such a way that I can still call it using Hero.fromAlias("Bruce Wayne"). So my enum classes would be simplified to something like:
enum class Hero(override val alias: String): AliasedEnum<Hero> {
SUPERMAN("Clark Kent"),
BATMAN("Bruce Wayne");
}
enum class Villain(override val alias: String): AliasedEnum<Villain> {
TWO_FACE("Harvey Dent"),
RIDDLER("Edward Nigma");
}
I attempted to incorporate the answer from Kotlin define interface for enum class values method, but couldn't see a way to access the enum values() from the companion object in the interface. Is there a clean way to do what I am wanting?
You can quite easily do this by using the fact that companion object objects can extend other classes.
Pretty much any solution will require two different parts, since you need:
A common interface that provides any data required for the function, so it is available regardless of the actual implementation.
A way to attach the shared function to the companion object for <Class>.function access. This can either be an abstract class with the required implementation or a marker class with the implementation as an extension function.
In the end the "cleanest" solution would probably be this:
// Attaching point for the extension function which provides the answer
interface EnumCompanion<T : Enum<T>>
// Marker interface to provide the common data
interface WithAlias {
val alias: String
}
inline fun <reified T> EnumCompanion<T>.fromAlias(
value: String
): T? where T : Enum<T>, T : WithAlias {
return enumValues<T>().find { it.alias == value }
}
// Define the enums and attach the helper to their companion object
enum class Hero(override val alias: String) : WithAlias {
SUPERMAN("Clark Kent"),
BATMAN("Bruce Wayne");
companion object : EnumCompanion<Hero>
}
enum class Villain(override val alias: String) : WithAlias {
TWO_FACE("Harvey Dent"),
RIDDLER("Edward Nigma");
companion object : EnumCompanion<Villain>
}
fun main() {
println(Hero.fromAlias("Bruce Wayne"))
println(Villain.fromAlias("Edward Nigma"))
}
I want to be able to save a class instance to a public static variable but I can't figure out how to do this in Kotlin.
class Foo {
public static Foo instance;
public Foo() {
instance = this;
}
}
Update: since this answer is getting a decent amount of upvotes, I really wanted to say that you shouldn't do the below, but instead just use object Foo { ... }, like Roman rightly points out in the comment.
Previous answer:
The closest thing to Java's static fields is a companion object. You can find the documentation reference for them here: https://kotlinlang.org/docs/reference/object-declarations.html#companion-objects
Your code in Kotlin would look something like this:
class Foo {
companion object {
lateinit var instance: Foo
}
init {
instance = this
}
}
If you want your fields/methods to be exposed as static to Java callers, you can apply the #JvmStatic annotation:
class Foo {
companion object {
#JvmStatic lateinit var instance: Foo
}
init {
instance = this
}
}
It looks that you want to define a singleton object. It is supported in Kotlin as a first-class concept:
object Foo {
...
}
All the boilerplate code with static field and constructor is taken care by the Kotlin automatically. You don't have to write any of that.
From the Kotlin code you can refer to the instance of this object simply as Foo. From the Java code you can referer to the instance of this object as Foo.INSTANCE, because the Kotlin compiler automatically creates the corresponding static field named INSTANCE.
first you create a simple class then after create a block followed by companion object keyword
for example:
class Test{
companion object{
fun getValue(): String{
return "Test String"
}
}
}
you can call this class function using class name dot function name
for example:
// here you will get the function value
Test.getValue()
You can create a companion object for the class, and if you want the field to be static you can use the annotation #JvmStatic. Companion object have access to private members of the class it is companion for.
See below an example:
class User {
private lateinit var name: String
override fun toString() = name
companion object {
#JvmStatic
val instance by lazy {
User().apply { name = "jtonic" }
}
}
}
class CompanionTest {
#Test
fun `test companion object`() {
User.instance.toString() shouldBe "jtonic"
}
}