Lets imagine we have sealed class with data classes and objects inside:
sealed class CarEvent {
data class FilledFuel(val fuelAmount: Float) : CarEvent()
data class ChangedPart(val part: CarPart) : CarEvent()
object StartedEngine : CarEvent()
}
Then we have when statement reducing events:
when(event) {
is CarEvent.FilledFuel -> { do something }
is CarEvent.ChangedPart -> { do something }
CarEvent.StartedEngine -> { do something }
}
The questing is: what is the most true approach for StartedEngine object:
compare it using is operator, or without is operator (using default == inside when statement).
Is there any difference between these two?
Is there any difference between these two?
Yes, is checks if something is an instance of a specific type, where as == will check if one object is the same as another
As an example:
val foo = 5
val result = foo is Int
This will always be true, because foo is a type of integer.
but if you consider
val foo = 5
val result = foo == 7
This will return false, correctly, because these two values aren't the same thing.
In your case, I don't think it would matter, but is would probably make more sense
When has a syntatic sugar of evaluating what's inside parentheses versus each case
When you put nothing, it checks for equality and in other cases it checks what you put there
In case of objects, kotlin creates a class for them and creates a singleton INSTANCE and if you try to look at it from a Java point of view, when you put is StartedEngine it's like checking event instanceof StartedEngine and when put StratedEngine it's like event.equals(StartedEngine.INSTANCE)
so one of them check for type while the other checks for equality
On the other hand, object are strictly singletons which you cannot inherit, or override anything from them so it doesn't matter if you check for equality or is unless you've override equals in that object.
So they're not different in usage, nor in action. They are just different approaches.
sealed class A{
abstract val name:String
object AAA : A(){
override val name get()= "AA"
override fun equals(o: Any?) : Boolean {
println("AAA equality call")
return super.equals(o)
}
}
object BBB : A() {
override val name get()="BB"
}
}
fun main() {
val o : A = A.AAA
when(o){
is A.AAA -> println("o is AAA")
}
when(o){
A.AAA -> println("o==AAA")
}
}
But notice that if the override fun equals in AAA returned false, o==AAA wouldn't get printed also note that in second when, equals gets called and it prints AAA equality call
Output
o is AAA
AAA equality call
o==AAA
Related
I have a bunch of beans that have nullable properties like so:
package myapp.mybeans;
data class Foo(val name : String?);
And I have a method in the global space like so:
package myapp.global;
public fun makeNewBar(name : String) : Bar
{
...
}
And somewhere else, I need to make a Bar from the stuff that's inside Foo. So, I do this:
package myapp.someplaceElse;
public fun getFoo() : Foo? { }
...
val foo : Foo? = getFoo();
if (foo == null) { ... return; }
// I know foo isn't null and *I know* that foo.name isn't null
// but I understand that the compiler doesn't.
// How do I convert String? to String here? if I do not want
// to change the definition of the parameters makeNewBar takes?
val bar : Bar = makeNewBar(foo.name);
Also, doing some conversion here with foo.name to cleanse it every time with every little thing, while on the one hand provides me compile-time guarantees and safety it is a big bother most of the time. Is there some short-hand to get around these scenarios?
You need double exclamation mark like so:
val bar = makeNewBar(foo.name!!)
As documented in Null Safety section:
The third option is for NPE-lovers. We can write b!!, and this will
return a non-null value of b (e.g., a String in our example) or throw
an NPE if b is null:
val l = b!!.length
Thus, if you want an NPE, you can have it, but you have to ask for it explicitly, and it does not appear out of the
blue.
You could use an extension:
fun <T> T?.default(default: T): T {
return this ?: default
}
Then use it like this:
fun getNonNullString(): String {
return getNullableString().default("null")
}
In my game I have two players and so I defined a typealias of pair that should indicate that I have two things of the same type and .first belongs to player one and .second belongs to player two:
typealias PlayerPair<A> = Pair<A, A>
Also I have defined this enum class
enum class PlayerNumber {
One,
Two
}
Now i would like to add an operator (as extension function) to access the elements like this
myPair[Player.One]
That is my approch but it doesn't work
operator fun PlayerPair.get(i: PlayerNumber) = when (i) {
PlayerNumber.One -> PlayerPair.first
PlayerNumber.Two -> PlayerPair.sexond
}
Adding an else Branch removes one error, but I don't understand why it is necessary.
The other error is 'One type argument expected for typealias PlayerPair = Pair'.
But I can't figure out what this means in this context.
You can create a generic get function, you just have to specify a type parameter, and that you're extending a PlayerPair<T>:
operator fun <T> PlayerPair<T>.get(i: PlayerNumber): T = when (i) {
PlayerNumber.One -> this.first
PlayerNumber.Two -> this.second
}
Inside the operator, you can refer to the instance of PlayerPair<T> as this, which you can also use implicitly:
operator fun <T> PlayerPair<T>.get(i: PlayerNumber): T = when (i) {
PlayerNumber.One -> first
PlayerNumber.Two -> second
}
In kotlin, you can use the reference operator to get the KProperty of a package extension property like this:
val String.extProp: String
get() = "Some get code"
fun foo() {
val prop: KProperty<String> = String::extProp
}
However, when the extension property is declared inside a class the reference operator no longer works:
class Example() {
val String.extProp: String
get() = "Some get code"
fun foo() {
val prop: KProperty<String> = String::extProp // error
}
}
So what I am wondering is how can I change the problematic line in the second example, so the KProperty is gotten?
The error you are getting is:
Error:(y, x) Kotlin: 'extProp' is a member and an extension at the same time. References to such elements are not allowed
There is no syntax mechanism to generate a reference to an extension method that also requires a containing class. Your extension for example might use members of the class, and this would need something like "bound references" coming in Kotlin 1.1 (which I'm not sure will cover this case either, it is currently an open question). So for now, there is no :: syntax available. Things like Example::String::extProp are not available, neither is the commonly tried Example::String.extProp syntax. But you can find it by reflection.
First you need to know the type you will receive is:
KProperty2<INSTANCE, EXTENDING, PROPTYPE>
Whereas a normal property on a class is:
KProperty1<INSTANCE, PROPTYPE>
You need to know that because any call to the getter will require the class instance and an instance of the class the property is extending. So you cannot call it the same way as you would a property reference of a class.
You can use this function to find an extension property declared in a class:
#Suppress("UNCHECKED_CAST")
fun <T: Any, EXTENDING: Any, R: Any> KClass<T>.extProp(extends: KClass<EXTENDING>, name: String, returning: KClass<R>): KProperty2<T, EXTENDING, R> {
return this.declaredMemberExtensionProperties.first {
it.name == name &&
it.parameters.size == 2 &&
it.parameters[0].kind == KParameter.Kind.INSTANCE && it.parameters[0].type == this.defaultType &&
it.parameters[1].kind == KParameter.Kind.EXTENSION_RECEIVER && it.parameters[1].type == extends.defaultType &&
it.returnType == returning.defaultType
} as KProperty2<T, EXTENDING, R>
}
This is a bit overkill for the checking but ensures that it is future-proof in case any other types of extensions are added later. The following is your code updated to use it:
class Example() {
val String.extProp: String
get() = "howdy $this"
fun foo() {
val prop = Example::class.extProp(String::class, "extProp", String::class)
println(prop.get(this, "stringy")) // "howdy stringy"
}
}
I have a bunch of beans that have nullable properties like so:
package myapp.mybeans;
data class Foo(val name : String?);
And I have a method in the global space like so:
package myapp.global;
public fun makeNewBar(name : String) : Bar
{
...
}
And somewhere else, I need to make a Bar from the stuff that's inside Foo. So, I do this:
package myapp.someplaceElse;
public fun getFoo() : Foo? { }
...
val foo : Foo? = getFoo();
if (foo == null) { ... return; }
// I know foo isn't null and *I know* that foo.name isn't null
// but I understand that the compiler doesn't.
// How do I convert String? to String here? if I do not want
// to change the definition of the parameters makeNewBar takes?
val bar : Bar = makeNewBar(foo.name);
Also, doing some conversion here with foo.name to cleanse it every time with every little thing, while on the one hand provides me compile-time guarantees and safety it is a big bother most of the time. Is there some short-hand to get around these scenarios?
You need double exclamation mark like so:
val bar = makeNewBar(foo.name!!)
As documented in Null Safety section:
The third option is for NPE-lovers. We can write b!!, and this will
return a non-null value of b (e.g., a String in our example) or throw
an NPE if b is null:
val l = b!!.length
Thus, if you want an NPE, you can have it, but you have to ask for it explicitly, and it does not appear out of the
blue.
You could use an extension:
fun <T> T?.default(default: T): T {
return this ?: default
}
Then use it like this:
fun getNonNullString(): String {
return getNullableString().default("null")
}
In a method I would like to receive KMutableProperty as parameter and assign a value to it.
Another question is what is the correct way of passing a parameter into such a method.
Basically I would like to have something like that:
class MyBinder {
...
fun bind(property: KMutableProperty<Int>): Unit {
property.set(internalIntValue)
}
}
And then call it in another class
myBinder.bind(this::intProperty)
Kotlin 1.0 does not allow the this::intProperty syntax, but this is being worked currently and will be available soon as a part of the early access preview of 1.1 (issue, KEEP proposal).
With this in mind, I'd consider doing what you're describing in another way, for example making bind accept a lambda which sets the property:
class MyBinder {
fun bind(setProperty: (Int) -> Unit) {
setProperty(internalIntValue)
}
}
...
myBinder.bind { intProperty = it }
Anyway, to answer your question about setting the value of KMutableProperty: to set the value of some property or, technically speaking, to invoke the property setter, you should know its arity, or the number of parameters that property (and its getter/setter) accepts. Properties declared in a file do not accept any parameters, member properties and extension properties require one parameter (the receiver instance), while member properties which are also extensions take two parameters. These kinds of properties are represented by the following subtypes of KMutableProperty respectively: KMutableProperty0, KMutableProperty1, KMutableProperty2 -- the number means the arity and their generic type parameters mean the types of receivers. Each of these property types has a set method with the corresponding parameters. Some examples:
fun setValue(property: KMutableProperty0<Int>, value: Int) {
property.set(value)
}
fun setValue(property: KMutableProperty1<SomeType, Int>, instance: SomeType, value: Int) {
property.set(instance, value)
}
Note that there's no set (or get) method in the abstract KMutableProperty interface precisely because it's impossible to declare it, not knowing the number of required receiver parameters.
Additionally to Alexander's answer, you can try something like this:
import kotlin.reflect.KMutableProperty
class Binder {
val internalIntValue = 10
fun bind(self: Any, aProperty: KMutableProperty<Int>) {
aProperty.setter.call(self, internalIntValue)
}
}
class Foo {
var bar = 1
fun changeBar() {
Binder().bind(this, Foo::bar)
}
}
fun main(args: Array<String>) {
val foo = Foo()
assert(1 == foo.bar)
foo.changeBar()
assert(10 == foo.bar)
}
A more robust/safe way to do the same thing:
fun <T> bind(self: T, aProperty: KMutableProperty1<T, Int>) {
aProperty.set(self, internalIntValue)
}
My thanks to Alexander. His answer gave me the previous idea.