Setter overloading in Kotlin - kotlin

When trying to define a setter that accepts a parameter type that can be used to construct a property, thusly:
class Buffer(buf: String) {}
class Foo {
var buffer: Buffer? = null
set(value: String) {
field = Buffer(value)
}
}
I get the error message:
Setter parameter type must be equal to the type of the property
So what's meant to be the Kotlin way of doing this?

As of Kotlin 1.1 it is not possible to overload property setters. The feature request is tracked here:
https://youtrack.jetbrains.com/issue/KT-4075
Currently, you would have to define a buffer extension function on String:
val String.buffer : Buffer
get() = Buffer(this)
and set the value with
Foo().buffer = "123".buffer

Related

Retrieve Kotlin Property type from Kotlin psi API

I'm trying to create new rule for detekt project. To do this I have to know exact type of Kotlin property. For example, val x: Int has type Int.
Unfortunately, for property of type private val a = 3 I receive the following:
property.typeReference is null
property.typeParameters is empty
property.typeConstraints is empty
property.typeParameterList is empty
property.text is private val a = 3
property.node.children().joinToString() has object notation of previous item
property.delegate is null
property.getType(bindingContext) is null (the property bindingContext is part of KtTreeVisitorVoid used
Question: how can I get name of type (or, what is better, object KClass) to compare the actual property type with Boolean class type? (e.g. I just need to get if property boolean of not)
Code:
override fun visitProperty(property: org.jetbrains.kotlin.psi.KtProperty) {
val type: String = ??? //property.typeReference?.text - doesn't work
if(property.identifierName().startsWith("is") && type != "Boolean") {
report(CodeSmell(
issue,
Entity.from(property),
message = "Non-boolean properties shouldn't start with 'is' prefix. Actual type: $type")
)
}
}
Right solution:
fun getTypeName(parameter: KtCallableDeclaration): String? {
return parameter.createTypeBindingForReturnType(bindingContext)
?.type
?.getJetTypeFqName(false)
}
There are at least following values for Boolean type: kotlin.Boolean and java.lang.Boolean
Full code is here.

Acquire annotation parameters (or annotation instance) from Kotlin PSI

I have a Kotlin annotation:
#Retention(AnnotationRetention.SOURCE)
#Target(AnnotationTarget.CLASS)
annotation class Type(
val type: String
)
It can be used on the Kotlin classes in two ways: using the named parameter syntax, or using the positional parameter syntax:
#Type(type = "named")
data class Named(
…
)
#Type("positional")
data class Positional
…
)
I use this annotation in my custom detekt rules for some extra checks. I need to extract the value of the type parameter to perform some check based on it. I do that like:
private fun getType(klass: KtClass): String? {
val annotation = klass
.annotationEntries
.find {
"Type" == it?.shortName?.asString()
}
val type = (annotation
?.valueArguments
?.find {
it.getArgumentName()?.asName?.asString() == "type"
}
?.getArgumentExpression() as? KtStringTemplateExpression)
?.takeUnless { it.hasInterpolation() }
?.plainContent
return type
}
But this code works only with "named" parameters syntax, and fails for the positional one. Is there any way to get the value of an annotation parameter no matter what syntax was used? It would be perfect if I could acquire my Type annotation instance directly from PSI / AST / KtElements and use it like usually. Is it possible to instantiate an annotation from the PSI tree?

what is the meaning of the first line in the below kotlin code. Newbee to Kotlin from Java

I am a Java programmer and new to Kotlin. Please help me understand the below code, especially the first line.
class SiteListEventBus : EventBus<SiteListEventBus.SiteListChangeEvent, String, NotificationHandler<SiteListEventBus.SiteListChangeEvent>>() {
data class SiteListChangeEvent(val entityId: String, val routingKey: String)
override fun getSubscriptionKey(event: SiteListChangeEvent?): String {
return event!!.routingKey
}
}
class SiteListEventBus :EventBus<SiteListEventBus.SiteListChangeEvent, String,
NotificationHandler<SiteListEventBus.SiteListChangeEvent>>() {
So from what im gathering here EventBus would be like your base class which SiteListEventBus is inheriting from and EventBus which conforms to or includes 3 type parameters
Which are SiteListEventBus.SiteListChangeEvent as type 1,
String as type 2,
then NotificationHandler as type 3 which then has a type parameter of SiteListEventBus.SiteListChangeEvent little complicated there
data class SiteListChangeEvent(val entityId: String, val routingKey: String)
This data class then would just be the parameters/variables SiteListChangeEvent which would be your entityId of type string and your routingKey of type string
override fun getSubscriptionKey(event: SiteListChangeEvent?): String {
return event!!.routingKey
}
this last method overrides your getter for subscription key passes in your event which is SiteListChangeEvent? which is an optional value from the ? (so this can be null) to be used and its expecting a String for a return type
then your returning your passed in event!!.routingKey. the not-null assertion operator (!!) converts any value to a non-null type and throws an exception if the value is null.
So, you can write event!!, and this will return a non-null value of event (e.g., a String in your example) or throw a null pointer exception if event is null: soooo this seems like a bad idea because if event is null this will crash for sure
if you need further explanation let me know and ill go into further detail
Here is how I read the first line:
class SiteListEventBus
Define a new class.
: FooBar()
Extend the class FooBar using the empty constructor.
FooBar is actually EventBus<SiteListEventBus.SiteListChangeEvent, String, NotificationHandler<SiteListEventBus.SiteListChangeEvent>>
Generics apply here the way you would expect in Java.
class SiteListEventBus : FooBar() {
Begin implementing the SiteListEventBus class.
Here is how I read the rest:
data class SiteListChangeEvent(val entityId: String, val routingKey: String)
Create a data class.
override fun getSubscriptionKey
The override is similar to the #Override annotation. Override the method getSubscriptionKey.
event!!.routingKey
The event variable is nullable. I recommend reading about the !! operator.

Do we need to initialize nullable fields in kotlin?

I have recently reviewed some kotlin codes, All nullable field initialized as null.
What is the difference between val x : String? = null and val x : String?
Should we initialize the nullable fields as null?
Everything, even nullable variables and primitives, need to be initialized in Kotlin. You can, as tynn mentioned, mark them as abstract if you require overriding. If you have an interface, however, you don't have to initialize them. This won't compile:
class Whatever {
private var x: String?
}
but this will:
interface IWhatever {
protected var x: String?
}
This too:
abstract class Whatever {
protected abstract var x: String?
}
If it's declared in a method, you don't have to initialize it directly, as long as it's initialized before it's accessed. This is the exactly same as in Java, if you're familiar with it.
If you don't initialize it in the constructor, you need to use lateinit. Or, if you have a val, you can override get:
val something: String?
get() = "Some fallback. This doesn't need initialization because the getter is overridden, but if you use a different field here, you naturally need to initialize that"
As I opened with, even nullable variables need to be initialized. This is the way Kotlin is designed, and there's no way around that. So yes, you need to explicitly initialize the String as null, if you don't initialize it with something else right away.
A property must be initialized. Therefore you have to do the initialization var x : String? = null. Not assigning a value is only the declaration of the property and thus you'd have to make it abstract abstract val x : String?.
Alternatively you can use lateinit, also on non-nullable types. But this has the effect, that it's not null, but uninitialized lateinit var x : String.
val x : String? will create an uninitialized variable or property, depending on where it's defined. If it's in a class (rather than a function), it creates a property, and you cannot create an uninitalized property unless it's abstract. For example take this code:
class MyClass {
val x : String?
}
This won't compile. You'll get Property must be initialized or be abstract.
This code, however, will compile
class MyClass {
fun test() {
val x : String?
}
}
However it's a bit pointless as you will not be able to refer to that variable: as soon as you do you'll get Variable 'x' must be initialized.
So yes, generally when defining a nullable member you should initialize it (e.g. with a value of null), unless it's abstract, in which case the overriding class should initialize it.

Override setter for variable defined in default constructor

So I have a Kotlin class that looks something like this:
class MyClass {
var myString: String = ""
set(value) {
field = value
doSomethingINeed()
}
constructor(myString: String) {
this.myString = myString
}
}
However, Android Studio is warning me that I can use this as a default constructor. When I select that, it changes it to this:
class MyClass(var myString: String)
Now I lose the opportunity to override the setter, because if I make a method called setMyString() I'll get a compiler error.
Is there a way to override the setter if the field is part of the default constructor, or do I have to go with option 1 and just ignore the warning that I get?
The quick fix for it definitely screws things up but the comment is trying to point you in the correct direction. You want to define a primary constructor that accepts just a parameter (not defining a property) and then use that parameter for the property initialization. Something like this:
class MyClass(myString: String) {
var myString: String = myString
set(value) {
field = value
doSomethingINeed()
}
}