preface
From starting Kotlin, to trying to write some experience code, to doing some experimental packaging work, to finally writing a project. I have to say there were a few hicks along the way, although a few of them were due to my own choices, such as using anko layout instead of XML, but overall, the language surprised me enough to make me ignore the bumps in the road.
This article is just to sort out some feelings and surprises along the way. I will revise it in the long term as I learn and use Kotlin.
The body of the
1. With empty security, the server can no longer return empty objects
A simpler example would be String and String, right? There are two different types. String is not null, it must have a value; And the String? It’s unknown. It may have a value, it may be null. When using objects’ properties and methods, objects of type String can be used freely, whereas String? Type requires you to make a non-short judgment first.
fun demo() {
val string1: String = "string1"
val string2: String? = null
val string3: String? = "string3"println(string1.length) println(string2? .length) println(string3? .length) }Copy the code
The output is 7 null 7Copy the code
Although string2 is an empty object, it does not declare a null pointer just because I called its property/method. And all you have to do is add a “?” .
If this doesn’t show the benefits of air safety, consider the following example:
val a: A? = A() println(a? .b? .c)Copy the code
Imagine what we would have to do in JAVA when every level of property could be empty.
2. Transformation and intelligent transformation, save effort and worry
I’ve written JAVA code that looks like this
if(view instanceof TextView) {
TextView textView = (TextView) view;
textView.setText("text");
}
Copy the code
Kotlin’s writing rules are different
if(view is TextView) {
TextView textView = view as TextView
textView.setText("text")}Copy the code
The comparison is even more obvious when you shrink the code
JAVA
if(view instanceof TextView) {
((TextView) view).setText("text");
}
Kotlin
if(view is TextView) {
(view as TextView).setText("text")}Copy the code
Instead of adding (Class) to an object as JAVA does, Kotlin makes the transition by adding as Class after the object. At least personally, after getting used to the smooth writing style of AS Class, I can no longer bear the front writing style of JAVA. Even with the existence of cast shortcut keys, IT is still easy to interrupt the sequence and thinking of writing code
In fact, Kotlin could be simpler here:
if(view is TextView) {
view.setText("text")}Copy the code
Because the current context identifies a View as a TextView, the view in the current code block is no longer a View class, but a TextView class. This is Kotlin’s smart transformation.
So let’s take the empty security example above, and the general idea is, since String and String, right? It’s a different type, is it possible for me to write code like this?
val a: A? = A()
if(a ! = null) { println(a? .b) }Copy the code
Instead, Kotlin will show you a highlighted warning that this is an unnecessary safe call. And why? Because you already wrote a! = null, so a is no longer a in this code block, right? Type, but type A.
val a: A? = A()
if(a ! = null) { println(a.b) }Copy the code
Another scenario where smart transformations often occur is in switch Case statements. In Kotlin, it’s the when syntax.
fun testWhen(obj: Any) {
when(obj) {
is Int -> {
println("obj is a int")
println(obj + 1)
}
is String -> {
println("obj is a string")
println(obj.length)
}
else -> {
println("obj is something i don't care")
}
}
}
fun main(args: Array<String>) {
testWhen(98)
testWhen("98")}Copy the code
The following output is displayed: obj is an int 99 obj is a string 2Copy the code
I can use the.length property of the String class if it is an obj of Any class. In JAVA, we need to do this:
System.out.println("obj is a string")
String string = (String) obj;
System.out.println(string.length)
Copy the code
or
System.out.println("obj is a string")
System.out.println(((String) obj).length)
Copy the code
The e former interrupts the continuity of writing and reading, the latter.
Kotlin is far more intelligent than that. Even now, when I write code, I occasionally pop a highlighted warning, and I realize that my writing is redundant and Kotlin has taken care of it for me. I will not repeat them here.
3. When is more powerful than Switch
In the intelligent transformation example above, you have already shown some of the power of WHEN. But Kotlin’s when surprised me a lot more than JAVA’s Switch.
Such as:
fun testWhen(int: Int) {
when(int) {
in10.. Int.MAX_VALUE -> println("${int}It's too big for me to count.")
2, 3, 5, 7 -> println("${int}Is prime. "")
else -> println("${int}It's not prime.") } } fun main(args: Array<String>) { (0.. 10).forEach {testWhen(it) }
}
Copy the code
The output is as follows: 0 is not a prime 1 is not a prime 2 is a prime 3 is a prime 4 is not a prime 5 is a prime 6 is not a prime 7 is a prime 8 is not a prime 9 is not a prime 10 is too big for me to countCopy the code
Unlike JAVA’s rigid switch-case statement, in when I can match the range from 10 to int. MAX_VALUE with arguments, or 2, 3, 5, 7, without listing all the features. The flexibility and simplicity of When makes me happy to use it (compared to JAVA’s Switch).
4. Container operators
It’s hard to go back to RxJava since I fell in love with it, and that includes many of the convenient operators in RxJava. In Kotlin, the container itself comes with a set of operators that can be very succinct to implement some logic.
Such as:
(0 until container.childCount)
.map { container.getChildAt(it) }
.filter { it.visibility == View.GONE }
.forEach { it.visibility = View.VISIBLE }
Copy the code
The above code first creates a range from 0 to container.childcount-1; Use the map operator in conjunction with the code that fetches the child to convert the collection of ints to the collection of childViews; Select a new set from childView whose visibility is GONE by using the filter operator. Finally forEach traverses all childViews to be VISIBLE.
The JAVA code is pasted here for comparison.
for(int i = 0; i < container.childCount - 1; i++) {
View childView = container.getChildAt(i);
if(childView.getVisibility() == View.GONE) { childView.setVisibility(View.VISIBLE); }}Copy the code
I’m not going to go into the details of the advantages of this chain notation.
5. Thread switch, so easy
Now that WE’ve mentioned RxJava, we have to remember another advantage of RxJava — thread scheduling. Kotlin has a library specifically designed for Android development called Anko that contains a lot of code to simplify development, including threads.
async {
val response = URL("https://www.baidu.com").readText()
uiThread {
textView.text = response
}
}
Copy the code
The above code is very simple. The code is implemented in an asynchronous thread through async method. After reading the response to the HTTP request, the uiThread method is used to switch back to the UI thread and display the response on the textView.
Regardless of the internal implementation, you no longer need to write a lot of invalid code for a simple asynchronous task. By convention, this would seem to be the place to post JAVA code for comparison, but forgive me if I don’t want to flood the screen (lol)
6. Implement a singleton with a keyword
That’s right, a singleton can be implemented with a single keyword:
object Log {
fun i(string: String) {
println(string)
}
}
fun main(args: Array<String>) {
Log.i("test")}Copy the code
Goodbye, singleton pattern
Automatic getters, setters, and class concise declarations
JAVA has the following classes
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public void getName() {
return name;
}
}
Person person = new Person("Zhang");
Copy the code
As you can see, in standard writing, an attribute corresponds to both get and set methods, which requires a considerable amount of manual code. Of course there are shortcuts to help us generate this code, but it’s not perfect given the complexity.
And here’s Kotlin:
class Person(var name: String)
val person = Person("Zhang");
Copy the code
You can also add default values:
class Person(var name: String = "Zhang")
val person = Person()
Copy the code
I also attach a relatively complex data class in my project:
data class Column( var subId: String? , var subTitle: String? , var subImg: String? , var subCreatetime: String? , var subUpdatetime: String? , var subFocusnum: Int? , var lastId: String? , var lastMsg: String? , var lastType: String? , var lastMember: String? , var lastTIme: String? , var focus: String? , var subDesc: String? , var subLikenum: Int? , var subContentnum: Int? , var pushSet: String? )Copy the code
At first glance, there’s no extra code. This is one of the reasons why I think Kotlin code is easier to write cleanly than JAVA code.
8. DSL programming
Speaking of DSLS, Android developers probably have the most contact with Gradle
Such as:
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "com.zll.demo"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}}Copy the code
This is a Groovy DSL that declares compilation configurations
So what does it feel like to use a DSL in your Android project code?
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val homeFragment = HomeFragment()
val columnFragment = ColumnFragment()
val mineFragment = MineFragment()
setContentView(
tabPages {
backgroundColor = R.color.white
dividerColor = R.color.colorPrimary
behavior = ByeBurgerBottomBehavior(context, null)
tabFragment {
icon = R.drawable.selector_tab_home
body = homeFragment
onSelect { toast("home selected") }
}
tabFragment {
icon = R.drawable.selector_tab_search
body = columnFragment
}
tabImage {
imageResource = R.drawable.selector_tab_photo
onClick { showSheet() }
}
tabFragment {
icon = R.drawable.selector_tab_mine
body = mineFragment
}
}
)
}
Copy the code
Yes, the above code is used to build the main interface viewPager + Fragments + tabBar. Start with tabPages and set the background color, divider and other properties. Add fragment with tabButton (tabImage); All the code you see is configuration, and the implementation is wrapped up.
The anko library mentioned earlier can also be used as an alternative to XML for layout:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
verticalLayout {
textView {
text = "Here's the title."
}.lparams {
width = matchParent
height = dip(44)
}
textView {
text = "This is the content."
gravity = Gravity.CENTER
}.lparams {
width = matchParent
height = matchParent
}
}
}
Copy the code
In contrast to layout in JAVA code, this DSL approach also does configuration, encapsulating the implementation code behind the layout, which is very similar to XML layout.
There will be a future article on DSL and ANKO layouts, but I will stop here.
9. Delegate/agent, SharedPreference is no longer troublesome
With the delegate function in Kotlin, we can easily write a proxy class for Share Preference
class Preference<T>(val context: Context, val name: String? , val default: T) : ReadWriteProperty<Any? , T> { val prefs by lazy { context.getSharedPreferences("xxxx", Context.MODE_PRIVATE) } override fun getValue(thisRef: Any? , property: KProperty<*>): T = with(prefs) { val res: Any = when (default) { is Long -> { getLong(name, 0) } is String -> { getString(name, default) } is Float -> { getFloat(name, default) } is Int -> { getInt(name, default) } is Boolean -> { getBoolean(name, default) }else -> {
throw IllegalArgumentException("This type can't be saved into Preferences")
}
}
res as T
}
override fun setValue(thisRef: Any? , property: KProperty<*>, value: T) = with(prefs.edit()) { when (value) { is Long -> putLong(name, value) is String -> putString(name, value) is Float -> putFloat(name, value) is Int -> putInt(name, value) is Boolean -> putBoolean(name, value)else -> {
throw IllegalArgumentException("This type can't be saved into Preferences")
}
}.apply()
}
}
Copy the code
Let’s skip the principle for a moment and see how it works. Okay
class EntranceActivity : BaseActivity() {
private var userId: String by Preference(this, "userId"."")
override fun onCreate(savedInstanceState: Bundle?) {
testUserId()
}
fun testUserId() {
if (userId.isEmpty()) {
println("userId is empty")
userId = "default userId"
} else {
println("userId is $userId")}}}Copy the code
The command output is as follows: userId is empty userId is default userId userId is default userId...Copy the code
The userId retrieved from SharedPreference is empty when the app is first launched, but not empty after it. In this case, userId = “default userId” successfully changes the value of the SharedPreference.
That is, with the help of the Preference proxy, the SharedPreference access operation becomes as simple as ordinary object calls and assignments.
10. Extension, say goodbye to utility classes
A long, long time ago, someone told me that a utility class is itself a violation of object-oriented thinking. But then I thought, if you don’t let me use utility classes, how can I write some code? It wasn’t until I learned about expansion that the light fell on me.
fun ImageView.displayUrl(url: String?) {
if (url == null || url.isEmpty() || url == "url") {
imageResource = R.mipmap.ic_launcher
} else {
Glide.with(context)
.load(ColumnServer.SERVER_URL + url)
.into(this)
}
}
...
val imageView = findViewById(R.id.avatarIv) as ImageView
imageView.displayUrl(url)
Copy the code
The above code can be understood as:
1. I extended the ImageView class with a method called displayUrl that takes a String named URL. Class objects. If nothing else, the image of this URL will be loaded by Glide and displayed on the current imageView;
2. I got an instance of the ImageView class from findViewById somewhere else and called the ImageView displayUrl method to try to load the URL I passed in
Adding methods to ImageView by extension is less invasive than writing a CustomImageView and adding methods by inheriting ImageView. You don’t need to write the entire CustomImageView in code, and you don’t need to write the package name dead in the XML layout. Causing trouble with transplants.
You could do this with a utility class, such as imageutil.displayURL, but the utility class doesn’t read as well as the extended method reads more naturally and smoothly.
Extensions are Kotlin’s killer over JAVA