Recently I read about the smart cast performed by the is operator and also about the as or the better as?operators in that is used for explicit casting.
The kotlin docs species their difference of usage as follows:-
Note that smart casts do not work when the compiler cannot guarantee that the variable cannot change between the check and the usage. More specifically, smart casts are applicable according to the following rules:
val local variables - always except for local delegated properties;
val properties - if the property is private or internal or the check
is performed in the same module where the property is declared. Smart
casts aren't applicable to open properties or properties that have
custom getters;
var local variables - if the variable is not modified between the
check and the usage, is not captured in a lambda that modifies it,
and is not a local delegated property;
var properties - never (because the variable can be modified at any
time by other code).
Note that smart casts do not work when the compiler cannot guarantee
that the variable cannot change between the check and the usage.
The above written is bit confusing, since the var variables can be changed after initialization and I could not find a example that can make it clear the actual insight of the statement.
Can anyone make it easier to understand this insight better, in anyway?
And does is operator provide some optimization benefit over the as operator if, any?
The idea of a smart cast is to help you avoid using as or as? to explicitly cast something that was already checked. As far as the bullet points above, here are some examples.
val local variables - since a val is final (cannot be changed), after you do the check, the variable can be smart cast as it cannot change again.
val a: Int? = 2
if (a is Int) {
// 'a' is smart cast to Int
val b = a * 2 // b is also Int
}
val properties - if accessed directly (via the default getter), smart cast is possible. If through a custom getter, it's not because we cannot know it was modified.
class Test {
val a: Int? = 2;
}
class TestGetter {
val a: Int? = 2
get() = field * 2
}
// Usage
val test = Test()
val testGetter = TestGetter()
if (test.a is Int) {
// 'test.a' is smart cast to Int
val b = test.a * 2
}
if (testGetter.a is Int) {
// smart cast is impossible, 'testGetter.a' is still Int?
val b = testGetter.a * 2 // can't happen because we don't know whether 'a' was changed by the custom getter or not (the getter could give unstable values)
}
var local variables - if the variable is not modified between the check and the usage, is not captured in a lambda that modifies it, and is not a local delegated property;
var a: Int? = 2
if (a is Int) {
// 'a' was not changed, so it can be smart cast to Int
val b = a * 2 // b is also Int
}
var c = 4
if (c is Int) {
c = null
// 'c' was changed between the check and the usage, we cannot smart cast it anymore
val b = c * 2 // won't work
}
var properties - the var can always be modified by something else in the code, so smart cast won't work.
class Example {
var a: Int? = 2
fun test1() {
if (a is Int) {
// smart cast is impossible because we don't know whether 'a' was changed by some other code/function
val b = a * 2 // won't work
}
}
}
As far as using as goes, if you look at the last example:
class Example {
var a: Int? = 2
fun test1() {
if (a is Int) {
// smart cast is impossible because we don't know whether 'a' was changed by some other code/function
val b = a as Int * 2 // this WILL work because we forcefully cast it to Int, but if a is null there will be an exception in runtime
}
}
}
You can also use as? when you're not sure if the var can be cast to something or not. If not, it will just give you a null. For example:
val a: Double = 2.0
val b = a as? String // 'b' will be 'String?', in this case initialized to 'null' since 'a' cannot be cast to it
val c: Int? = 2
val d = c as? Int // 'd' will be '2' but still 'Int?' since 'as?' always makes the variable nullable
Hope the examples helped, let me know if I need to clarify something further.
Related
I want to know the difference between String? and String! in Kotlin.
I search on kotlinlang.org but did not find any information.
Kotlin's type system differentiates between nullable and non-nullable types. In that context, String? is a nullable type while String would be the corresponding non-nullable type.
When working with Java libraries, the compiler is not always able to identify whether a type is nullable or not, since Java does not have that differentiation. Such types will then show up as "platform type" String!, meaning (basically): "I have no idea if this can be null but I'll treat it as non-nullable for now".
If you have control over the corresponding Java library, Kotlin supports various annotations to help distinguish between types, otherwise it is up to you as developer to explicitly assign either a nullable or a non-nullable type e.g. upon variable declaration to avoid running into NullPointerExceptions at runtime.
I'll try to answer with some sample code.
1. String?
This means this string is nullable.
Example 1: Use it in the type definition.
fun testStringTypes() {
// When initializing stringA, we can set null as the value
var stringA: String? = null
// And we can also set it to a meaningful string
stringA = "Hello"
// Then we can still set it back to null
stringA = null
}
Example 2: a variance of String?
fun testStringTypes() {
var stringA: String? = null
stringA = "Hello"
stringA = null
val lenOfStringA = stringA?.length ?: 0
}
So here is a brief description about what this val lenOfStringA = stringA?.length ?: 0 means:
Because stringA is defined as nullable;
stringA?.length means, access to the length property only if stringA is not null;
Because if, when stringA is null and if the code still tries to access to length (like in Java), the program will throw a NullPointerException. stringA? a question mark here, is to avoid this, which is called SafeCalls.
2. String!
This is platform types.
Copy from the link above:
As mentioned above, platform types can't be mentioned explicitly in the program, so there's no syntax for them in the language. Nevertheless, the compiler and IDE need to display them sometimes (for example, in error messages or parameter info), so there is a mnemonic notation for them:
I think (correct me if I was wrong), this makes sense when working with Java, because String in Java can be null, in other words, when accessing it from Kotlin, we don't know it is null or not. So String! is kind of a reminder to developer: Hey! Attention, this variable could be null.
Example 3, work with Java method from Kotlin:
// In Java:
public class PlatformTypeTest {
public String returnSomeStringCouldBeNull() {
return null;
}
}
And let's call this method in Kotlin.
fun testStringTypes() {
val someStringFromJava = PlatformTypeTest().returnSomeStringCouldBeNull()
}
fun testStringTypes() {
val someStringFromJava = PlatformTypeTest().returnSomeStringCouldBeNull()
someStringFromJav
}
As we can see from above two screenshots, IDE is reminding us this String from Java can be null.
And for String!, we can access it in different ways:
fun main() {
val someStringFromJava = PlatformTypeTest().returnSomeStringCouldBeNull()
var lenOfString = someStringFromJava?.length ?: 0
// lenOfString = someStringFromJava.length // NullPointerException
// lenOfString = someStringFromJava!!.length // NullPointerException
println(lenOfString)
}
With code snippet above, it works fine with var lenOfString = someStringFromJava?.length ?: 0, but the other two ways will cause NPE, as explained at above.
String? is a nullable type.
String! is a platform type platform type.
From Kotlin website:
Nullable Types Example:
val nullable: String? = item // allowed, always works
val notNull: String = item // allowed, may fail at runtime
Platform Types Example:
- T! means "T or T?",
- (Mutable)Collection<T>! means "Java collection of T may be mutable or not, may be nullable or not",
- Array<(out) T>! means "Java array of T (or a subtype of T), nullable or not"
I want to reference an object within this class I have below:
class HerbData {
object Dill {
const val herbName: String = "This is Dill!"
const val scientificName: String = "Anethum Graveolens"
val dullThumbnail: Int = R.drawable.dill_thumbnail_attr
}
object Peppermint {
val herbName: String = "This is Peppermint!"
}
}
Is there anyway that I can reference the object by using a string in Kotlin? Here is somewhat what I mean:
HerbData."Dill".herbname
I can't find anything on this topic for Kotlin.
Another way you could do this is with an enum class. The advantage over a map is that you have a data structure you can reference directly in code, so you could use HerbData.Dill as well as HerbData["Dill"]. And that will enable you to take advantage of compile-time checking and lint warnings, refactoring, exhaustive pattern matching, code completion etc, because the data is defined in your code
enum class HerbData(
val herbName: String,
val scientificName: String? = null,
val dullThumbnail: Int? = null
) {
Dill("This is Dill!", "Anethum Graveolens", R.drawable.dill_thumbnail_attr),
Peppermint("This is Peppermint!");
companion object {
operator fun get(name: String): HerbData? =
try { valueOf(name) } catch(e: IllegalArgumentException) { null }
}
}
fun main() {
// no guarantee these lookups exist, need to null-check them
HerbData["Peppermint"]?.herbName.run(::println)
// case-sensitive so this fails
HerbData["peppermint"]?.herbName.run(::println)
// this name is defined in the type system though! No checking required
HerbData.Peppermint.herbName.run(::println)
}
>> This is Peppermint!
null
This is Peppermint!
Enum classes have that valueOf(String) method that lets you look up a constant by name, but it throws an exception if nothing matches. I added it as a get operator function on the class, so you can use the typical getter access like a map (e.g. HerbData["Dill"]). As an alternative, you could do something a bit neater:
companion object {
// storing all the enum constants for lookups
private val values = values()
operator fun get(name: String): HerbData? =
values.find() { it.name.equals(name, ignoreCase = true) }
}
You could tweak the efficiency on this (I'm just storing the result of values() since that call creates a new array each time) but it's pretty simple - you're just storing all the enum entries and creating a lookup based on the name. That lets you be a little smarter if you need to, like making the lookup case-insensitive (which may or may not be a good thing, depending on why you're doing this)
The advantage here is that you're generating the lookup automatically - if you ever refactor the name of an enum constant, the string label will always match it (which you can get from the enum constant itself using its name property). Any "Dill" strings in your code will stay as "Dill" of course - that's the limitation of using hardcoded string lookups
The question really is, why do you want to do this? If it's pure data where no items need to be explicitly referenced in code, and it's all looked up at runtime, you should probably use a data class and a map, or something along those lines. If you do need to reference them as objects within the code at compile time (and trying to use HerbData."Dill".herbName implies you do) then an enum is a fairly easy way to let you do both
Declare a Data Class
data class HerbData (
val scientificName: String,
val dullThumbnail: Int
)
Initialize a muteable map and put data in it
val herbData = mutableMapOf<String, HerbData>()
herbData.put("Dill", HerbData("Anethum Graveolens", R.drawable.dill_thumbnail_attr))
herbData.put("Peppermint", HerbData("Mentha piperita", R.drawable.peppermint_thumbnail_attr))
You can now just
herbData["Dill"]?.scientificName
class HerbData {
interface Herb {
val herbName: String
val scientificName: String
}
object Dill : Herb {
override val herbName: String = "This is Dill!"
override val scientificName: String = "Anethum Graveolens"
}
object Peppermint: Herb {
override val herbName: String = "This is Peppermint!"
override val scientificName: String = "Mentha piperita"
}
companion object {
operator fun get(name: String): Herb? {
return HerbData::class
.nestedClasses
.find { it.simpleName == name }
?.objectInstance as? Herb
}
}
}
println(HerbData["Dill"]?.herbName) // Prints: This is Dill!
println(HerbData["Peppermint"]?.scientificName) // Prints: Mentha piperita
println(HerbData["Pepper"]?.herbName) // Prints: null
while building a simple currency converter app in android kotlin I got the following error at * operator ? what could be reason for the error.
You cannot use round() function with nullable Any and Float variables. Also you need to convert Any to Float.
Try to convert them with this example:
var rate : Any? = 5
var fromAmount : Float? = 3.5f
val result = round(rate!!.toString().toFloat() * fromAmount!!)
Seems that rate variable is nullable (float? I suppose), while multiplying operation work with non null values only.
Also smart cast wasn't used here, so rate variable seems to be changeable. So I recommend to make it unchangeable (val variable), or use buffer variable like following:
val bufferRate = rate
if(bufferRate == null) {
...
} else {
val convertedCurrency = fromAmount * bufferRate //here `bufferRate` should be smart casted to non nullable type
}
I'm on learning basics of Kotlin language for Android Development :), i'm looking on some Kotlin syntax examples and i found out this example below.. the thing that i don't know what is purpose with the ? code.. i'm trying to find on Google but i can't understand completely what they explain, maybe you can help me with your explanation / your example on this one ? I'll appreciate that :)
Example :
fun main() {
val myName :String? = "Maura"
println("Your name is $myName")
val myAge :Int? = 18
println("Your age is $myAge")
}
Its null safety. Basically Int? represents nullable Int, while Int on its own is non-null.
var a: Int = 5
a = null // error
var b: Int? = 5
a = null // :)
In case you have null in your code, you cannot use them directly, if you want to for example call any function in them you have to follow null-safety: Use ?. safecall operator, or !!. null assertion operator (usually shouldn't use).
val c = a.plus(4) // :)
val d = b.plus(4) // Hold on, I'm null you can't use "." on me :P
val e = b?.plus(4) // Ok, if b is not null, add 4 and store that to e, otherwise store null to e
val f = b!!.plus(4) // Hmm, if b was not null I'll add 4 to it and store to f, otherwise I'll crash your program throwing NPE
In contrast to this the type of e would be Int? as you've already read the thing. But what if you want to assign it a default value, easy use the elvis operator (?:):
val g = b?.plus(4) ?: 4 // if b is not null add 4 to it and store otherwise store 4 :)
Edit: The code in your sample works because there's String template calls .toString() and it is defined as fun Any?.toString() i.e. defined on a nullable receiver. So b.toString is valid though can be confusing.
println("Your age is $myAge")
// is same as
println("Your age is" + myAge.toString())
// toString is an extension function valid for nullable types, though member functions aren't :)
well, this is null safety feature of Kotlin lang (awesome btw.)
in short: ? next to type of variable/value means that this variable/value may be null. don't use it so often, it kind-of protects you from NullPointerExceptions, pretty often bug-cause in Java. also in your simple case it is unnecessary
var myName :String? = null // this is possible
var mySecondName :String = null // this isn't possible, build error
var myThirdName :String = "never null"
myThirdName = null // build error, this variable can't be null
myName = myName.replace("a", "b")
// build error above, trying to access nullable variable straightly
myName = myName?.replace("a", "b")
// null safety with ?, if myName is null it won't be executed and myName stay null
myThirdName = myThirdName.replace("a", "b") // this is possible
myThirdName = myThirdName?.replace("a", "b")
// this is also possible, but you will get a warning that ? is unnecessary
I've been using Kotlin to develop some apps in Android and what i want to do currently is to set a field value inside the defining class without calling the setter method.
Here is the code inside my class:
var projectList: List<Project>? = null
set(value) {
saveProjects(value as ArrayList<Project>)
field = value
}
//GO to the database and retrieve list of projects
fun loadProjects(callback: Project.OnProjectsLoadedListener) {
database.projectDao().getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ success ->
callback.onProjectsLoaded(success)
//Here i don't want to save the projects, because i've loaded them from the database
this.projectList = success
},
{ error -> GenericErrorHandler.handleError(error,callback.retrieveContext())}
)
}
Does anybody know a way to do this without calling the set(value) method?
You can only gain access to the field directly from the setter. Inside a setter, the field can be accessed through the invisible field variable.
There are perhaps some other ways around your requirements though. Here are 2 examples. You wouldn't have to follow them exactly, but could instead also combine them to make whatever solution you want.
You could use another shell property to act as the setter for your actual property:
class Example1 {
var fieldProperty: Int = 0
var setterPropertyForField: Int
get() = fieldProperty
set(value) {
fieldProperty = value
}
}
You could use setters as you actually would in Java with a JVM field and a set method. The #JvmField is probably not necessary.
class Example2 {
#JvmField var fieldProperty: Int = 0
fun setField(value: Int) {
fieldProperty = value
}
}
You could probably access the field and change it through reflection, but I don't recommend that approach. That would likely only lead to problems.