可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I wonder what is the best way to handle such scenario
class Person(var name:String? = null, var age:Int? = null){
fun test(){
if(name != null && age != null)
doSth(name, age) //smart cast imposible
}
fun doSth (someValue:String, someValue2:Int){
}
}
What is the simplest way to call doSth method and making sure that name and age are nt null?
I am looking for something simple as with one variable scenario where I would simply use let
name?.let{ doSth(it) }
回答1:
You can nest let
as much as you like so:
fun test(){
name?.let { name ->
age?.let { age ->
doSth(name, age) //smart cast imposible
}
}
}
Another approach, that might be easier to follow, is to use local variables:
fun test(){
val name = name
val age = age
if(name != null && age != null){
doSth(name, age)
}
}
Last but not least, consider changing Person
to be immutable like so:
data class Person(val name:String? = null, val age:Int? = null){
fun test(){
if(name != null && age != null){
doSth(name, age)
}
}
...
}
回答2:
For the cast to be possible you have to make a local copy of the value somehow. In Kotlin this is best done explicitly:
val name = name
val age = age
if(name != null && age != null){
doSth(name, age)
}
The let
function hides this behind an abstraction layer, which is not the best IMHO.
回答3:
There's a nice, little lib that allows for writing let
-like code with multiple variables. It's open-source and you can find it on GitHub, it's called Unwrap
Example based on readme:
unwrap(_a, _b, _c) { a, b, c ->
println("$a, $b$c") // all variables are not-null
}
All unwrap(...)
methods are marked inline
so there should be no overhead with using them.
By the way, this lib also allows to handle situation when there are some null variables (the nah()
method).
回答4:
If you want to take it a little "extreme" you could define an extension function on Pair<String?,Int?>
that hides the logic for you:
fun Pair<String?,Int?>.test(block: (String, Int) -> Unit) {
if(first != null && second != null) {
block(first, second)
}
}
then, calling it will be a little more concise
(name to age).test { n, a ->
println("name: $n age: $a")
}
However, it won't really help you (since you could as well define this as a function inside the Person
class itself), unless you need this kind of functionality really often throughout the whole project. Like I said, it seems overkill.
edit
you could actually make it (a little) more useful, by going fully generic:
fun <T,R> Pair<T?,R?>.ifBothNotNull(block: (T, R) -> Unit) {
if(first != null && second != null){
block(first, second)
}
}
回答5:
In addition to miensol's answer there are various ways to copy property values into function variables to enable smart cast. e.g.:
Intermediary function:
class Person(var name: String? = null, var age: Int? = null) {
fun test() = test(name, age)
private fun test(name: String?, age: Int?) {
if (name != null && age != null)
doSth(name, age) //smart cast possible
}
fun doSth(someValue: String, someValue2: Int) {
}
}
Anonymous function:
class Person(var name: String? = null, var age: Int? = null) {
fun test() = (fun(name: String?, age: Int?) {
if (name != null && age != null)
doSth(name, age) //smart cast possible
})(name, age)
fun doSth(someValue: String, someValue2: Int) {
}
}
Default arguments:
class Person(var name: String? = null, var age: Int? = null) {
fun test(name: String? = this.name, age: Int? = this.age) {
if (name != null && age != null)
doSth(name, age) //smart cast possible
}
fun doSth(someValue: String, someValue2: Int) {
}
}
回答6:
It is possible to define an inline method that allows you to take N parameters in order to avoid nesting let
s (I'm basing my answer on this).
inline fun <T1: Any, T2: Any, R: Any> safeLet(p1: T1?, p2: T2?, block: (T1, T2)->R?): R? {
return if (p1 != null && p2 != null) block(p1, p2) else null
}
Then
fun test() {
safeLet(name, age, {name, age ->
doSth(name, age) //smart cast
});
}
回答7:
I was having the problem while assigning text to textview with same problem description.
All I did was putting double exclamation mark after the name of my textview.
For example:
var name:TextView?=null
name = findViewById(R.id.id_name)
name!!.text = "Your text"