C# has a really nice feature called nameof. With this expression you can get the name of a program element in a safe way without repeating it as a string.
Example:
fun process(customer: Customer) {
checkArgument(customer.isTrustworthy, "customer must be trustworthy")
...
}
Here you have to repeat the parameter name customer in the exception message. That is error prone.
It would be nice to have a feature like nameof here:
fun process(customer: Customer) {
checkArgument(customer.isTrustworthy, "${nameof(customer)} must be trustworthy")
...
}
No, we donât have anything like that on the roadmap. Given that IntelliJ IDEAâs refactorings do a fairly good job of updating string literals, adding a special language feature just for this case seems to be a bit overkill.
Absolutely agree with @ilya.gorbunov. Ruby has long had the :thing syntax which is used wildley different than âthingâ.
Semantism is a core pinnacle of strongly-typed languages which Kotlin strongly firmly in. To suggest a string literal solution and leave it up to âIDE refactoringâ is literally against the whole belief system which Kotlin is built upon, let alone being a huge known Magic Strings anti-pattern. This is why we have enums and the strongly-typed âwhenâ (Kotlin) / âswitchâ (Swift) statements, to get AWAY from string-based, non-typed workarounds.
I wouldnât expect this answer from an intermediate developer, let alone the you guys.
This feature actually exists for properties and function names, but not for local variables (yet). Try this:
class Data(val namedProp: String)
fun namedFunction(): String = ""
fun main(args: Array<String>) {
println("My property name is '${Data::namedProp.name}'")
println("My function name is '${::namedFunction.name}'")
}
Compiler actually inlines the corresponding invocations and substitutes the actual names, so there is no runtime reflection in this code.
P.S. There is also a minor performance problem related to existing .name support in compiler, but that is not the reason for not using this feature if you needed it, and definitely not a reason to invent any kind of nameof (because .name is the Kotlin way!): https://youtrack.jetbrains.com/issue/KT-16304
Obviously, repeating the name is not super convenient. I could imagine, if magicallyDefine is an inline function, it could potentially access the outer variable name using similar syntax and get the String using the proposed .name semi-reflection:
val `Some Kafka Topic Name` = magicallyDefine<Key, Value>()
Where the name is deducted using syntax like val topic: String = ::return.name, which looks amazingly bad and is not always available.
I guess at this level of complexity it is much better to make a normal named class.
Another use case is to retrieve names of object properties/functions. Something like nameOf(a::foo).
I have created a feature request: https://youtrack.jetbrains.com/issue/KT-23777
Agree too. one must remember to run a refactoring. With nameof more is automatic. Big value proposition of Kotlin is that there is actually innovation and new features being added. This is one that unquestionably should be added. I use this a lot in C#. it is a great feature.
Out of curiosity, whatâs wrong with some first-class functions to handle this?
fun nameOf(callable: KCallable<*>): String = callable.name
fun nameOf(clazz: KClass<*>): String = clazz.simpleName!!
fun nameOf(clazz: Class<*>): String = clazz.simpleName
Use like soâŠ
data class MyAwesomeClass(val myAwesomeProperty: Int) {
fun helloWorld() = Unit
}
fun main(vararg args: String) {
println(nameOf(MyAwesomeClass::class))
println(nameOf(MyAwesomeClass::myAwesomeProperty))
println(nameOf(MyAwesomeClass::helloWorld))
}
Works for classes, properties, functionsâŠthe only case Iâve found where it wonât work is local fields.
The idea is not bad, but it wonât work for parameter names. And it would add some (not too big, I think) runtime overhead whereas a nameof operator would be evaluated at compile time.
This would be ideal for use in some annotation parameters. Since annotation parameters with interpolated strings (with compile time constants) are already supported, nameof would be very useful in these circumstances. For example, with AspectJ:
// This predictably fails because MyAnnotation::class.simpleName is not
// a compile-time constant
@Around("@annotation(${MyAnnotation::class.simpleName})")
// This would presumably work, as nameof(MyAnnotation) should resolve
// to a compile-time constant
@Around("@annotation(${nameof(MyAnnotation)})")
I think this requirement is just to avoid hard coding.
So I wrote a KSP project to temporarily solve the problem of hard coding with constant names. Use a @Konst annotation on the declarations that you want to use its name.
@Konst class MyClass
The variable MyClass_qName and MyClass_sName will be generated at the top-level in the same package of MyClass.
See more information at my toy project: GitHub - XYZboom/konst-names.
Yeah. Besides the oblivious lack of support for function parameters all those .name references are basically useless in annotations where only constants are acceptable.