Author of this issue:
Video: Throwing the line (Zhu Kai)
Article: Walker zhang Lei
Hi, I’m Junkey the sling. This is the second installment of the basics of Kotlin: “It doesn’t Write that way” in Kotlin. No more words, video service.
Since I never learned how to post a video on the Nuggets, click here to bilibili, or click here to YouTube.
The following is from the author Walker.
Last time we talked about the basics of Kotlin: variables, functions, and types. You’ve heard that Kotlin is fully compatible with Java, which means that code written in Java and Kotlin interact perfectly, not that you can write Kotlin in Java, which is not. In this video, we’re going to talk about the non-Java ways of writing Kotlin.
Constructor
The Kotlin constructor was introduced briefly in the last article. This section takes a look at how Kotlin’s constructor is different from Java’s:
-
Java
β οΈpublic class User { int id; String name; π π public User(int id, String name) { this.id = id; this.name = name; }}Copy the code
-
Kotlin
π οΈclass User { val id: Int valName: String πconstructor(id: Int, name: String) { / / π not public this.id = id this.name = name } } Copy the code
Two differences can be found:
- In Java, the constructor has the same name as the class, which is used in Kotlin
constructor
Said. - The constructor in Kotlin does not have the public modifier, because by default visibility is public. (I won’t expand the visibility modifier here, but I’ll get to that later.)
init
In addition to the constructor, the init block, which is often used together in Java, is written a little differently in Kotlin: you need to prefix it with init.
-
Java
β οΈpublic class User {π {// Initialize the code block before executing the following constructor } public User(a) {}}Copy the code
-
Kotlin
π οΈclass User {π init {// Initialize the code block before executing the following constructor } constructor() {}}Copy the code
As noted above, Kotlin’s init block is executed at instantiation time, just like Java, and it is executed before the constructor.
As mentioned in the last article, Java classes without the final keyword can be inherited by default, while Kotlin’s classes are final by default. In Java, final can also be used to modify variables. Let’s take a look at how Kotlin does this.
final
Val in Kotlin, like final in Java, represents a read-only variable and cannot be modified. Here’s a comparison of Java’s member variables, parameters, and local variables:
-
Java
β οΈ πfinal int final1 = 1; π void method(final String final2) {πfinal String final3 = "The parameter is " + final2; } Copy the code
-
Kotlin
π οΈ πval fina1 = 1 // the π parameter has no val fun method(final2: String){πval final3 = "The parameter is " + final2 } Copy the code
We can see the main differences are:
- Final becomes val.
- Kotlin’s arguments are of type val by default, so you don’t need to write the val keyword before the arguments. The reason for this design in Kotlin is to ensure that the arguments can’t be modified, whereas Java’s arguments can be modified (not final by default), which increases the probability of an error.
As we said in the last video, var is short for variable, and val is short for value.
In fact, when we’re writing Java code, very few people use final, but final is useful for modifying variables, but nobody uses it; But if you look at Kotlin code written by people, both domestic and foreign, you’ll find a lot of people have a bunch of Val’s in their code. Why is that? Because final is a bit more difficult to write than val: I need to write one more word. It’s just a little bit of trouble, but it causes a lot of people not to write.
That’s the interesting thing: going from final to val, which is just a little bit more convenient, makes a huge difference in how often it’s used. This change affects the quality of the code: by adding restrictions where appropriate, you reduce the probability of code errors.
val
Custom getter
There is a difference between val and final. Although val can’t be assigned twice, you can use a custom getter function to return a dynamically obtained value each time the variable is accessed:
π οΈ πval size: Int
get() { // π Executes kitems.size every time the size value is fetched
return items.size
}
Copy the code
But this is another way of using val, which in most cases corresponds to final use in Java.
static
property / function
So we said that nobody likes to write final, right? But there’s one scenario where people like to use final the most: constants.
β οΈpublic static final String CONST_STRING = "A String";
Copy the code
When we write constants in Java, we use static + final. And in Kotlin, in addition to final, static is written differently, and it’s even more different. To be exact: in Kotlin, the concepts of static variables and static methods are eliminated.
What if you want to use a direct reference in Kotlin through a class like in Java? Kotlin’s answer is Companion Object:
π οΈclass Sample {... πcompanion object {
val anotherString = "Another String"}}Copy the code
Why does Kotlin get more complicated? Let’s see what “object” is.
object
Object in Kotlin — lowercase, not uppercase, object in Java is no longer used in Kotlin.
Object in Java becomes Any in Kotlin, and acts just like Object: as the base class for all classes.
While object is not a class, it is a keyword in Kotlin like class:
π οΈobject Sample {
val name = "A name"
}
Copy the code
The idea is straightforward: create a class, and create an object of that class. This is what object means.
To use this object in your code, you can access it directly by its class name:
π οΈ Sample. The nameCopy the code
That’s what singletons are, so creating singletons in Kotlin doesn’t have to be as complicated as creating singletons in Java. You just replace class with object.
-
Singleton class
Let’s look at an example of a singleton, implemented in Java and Kotlin:
-
Implementations of singletons in Java (non-thread-safe) :
β οΈpublic class A { private static A sInstance; public static A getInstance(a) { if (sInstance == null) { sInstance = new A(); } return sInstance; } // π has a lot of template code. }Copy the code
You can see that Java has written a lot of template code to implement the singleton class, which is a little tedious.
-
Implement singletons in Kotlin:
π οΈ// π Class is replaced with object object A { val number: Int = 1 fun method(a) { println("A.method()")}}Copy the code
The differences between Java and Java are:
- Similar to the class definition, but put
class
Replaced with aobject
γ - There is no need to maintain an additional instance variable
sInstance
. - There is no need to “ensure that the instance is created only once”
getInstance()
Methods.
It’s much simpler than the Java implementation.
This singleton implemented by Object is a hunger-like singleton and is thread safe.
- Similar to the class definition, but put
-
-
Inheriting classes and implementing interfaces
In Kotlin, not only can a class inherit from another class, but also an object can implement an interface:
π οΈopen class A { open fun method(a){... }}interface B { fun interfaceMethod(a) } π π π object C : A(), B { override fun method(a){... }override fun interfaceMethod(a){... }}Copy the code
Why do objects implement interfaces? Object is a combination of two steps into one step. It has the class keyword function and implements the singleton, so it is easy to understand.
-
An anonymous class
In addition, Kotlin can also create anonymous classes in Java, but the writing is a little different:
-
Java:
β οΈ π ViewPager. SimpleOnPageChangeListener listener =new ViewPager.SimpleOnPageChangeListener() { @Override / / π public void onPageSelected(int position) { // override}};Copy the code
-
Kotlin:
π οΈval listener = object: ViewPager.SimpleOnPageChangeListener() { override fun onPageSelected(position: Int) { // override}}Copy the code
This is similar to how Java creates anonymous classes, except that new is replaced by object: :
- In Java
new
An object used to create an anonymous class - In the Kotlin
object:
It can also be used to create objects of anonymous classes
Both new and object: refer to interfaces or abstract classes.
- In Java
-
companion object
Variables and functions in an object decorated with object are static, but sometimes we just want some of the functions and variables in the class to be static.
π οΈclass A {πobject B {
var c: Int = 0}}Copy the code
As mentioned above, we can create an object in the class and place the variable or function that needs to be static in the internal object B. The external static variable can be called as follows:
π οΈ A.B.C πCopy the code
Objects nested in a class can be modified with companion:
π οΈclass A {πcompanion object B {
var c: Int = 0}}Copy the code
Companion can be understood as companion, companion, and represents a decorated object and external class binding.
But there is a small limitation: you can have at most one companion object in a class, but you can have multiple nested objects. There are three thousand beauties in the emperor’s harem, but only one empress.
The advantage of this is that the name of the object can be omitted:
π οΈ A.c// π B is gone
Copy the code
So, when the companion modifier is used, the name of the object can also be omitted:
π οΈclass A {
// π B is gone
companion object {
var c: Int = 0}}Copy the code
That’s the equivalent of Java static variables and methods that we talked about at the beginning of this section: Companion Object variables and functions.
-
Static initialization
Static variables and methods in Java are placed in Companion Objects in Kotlin. So static initializations in Java are naturally placed in Companion Objects in Kotlin, just like class initialization code, represented by init and a pair of curly braces:
π οΈclass Sample {πcompanion object{π init {... }}}Copy the code
Top-level property/function declaration
Instead of just calling static functions, Kotlin has something even more convenient: “top-level declaration.” Is actually the attributes and functions of statement don’t write in the class, this is allowed in the Kotlin:
π οΈpackage com.hencoder.plus
// π belongs to package, not in class/object
fun topLevelFuncion(a){}Copy the code
Properties and functions written in this way do not belong to any class, but directly belong to the package. They are as global as static variables and functions, but are easier to use: You don’t even need to write the class name when you use them elsewhere:
π οΈimport com.hencoder.plus.topLevelFunction // π Import function directly
topLevelFunction()
Copy the code
Writing a function or variable at the top level has an advantage: When you write code in Android Studio, it’s easy for the IDE to automatically think of the function you’re writing based on the first few letters. This improves the efficiency of writing code and reduces duplication of code in your project.
-
Name the same top-level function
One possible problem with not writing top-level functions in a class is that if you declare functions with the same name in different files, will they get confused? Here’s an example:
-
In the org.kotlinmaster.library package there is a function method:
π οΈpackageOrg. Kotlinmaster. Library1 πfun method(a) { println("library1 method()")}Copy the code
-
In the org. Kotlinmaster. Library2 package under a same function:
π οΈpackageOrg. Kotlinmaster. Library2 πfun method(a) { println("library2 method()")}Copy the code
What happens if you call both of these functions at the same time:
π οΈimportOrg. Kotlinmaster. Library1. Method πfun test(a){method () π org. Kotlinmaster. Library2. Method ()}Copy the code
As you can see, when there are two top-level functions with the same name, the IDE automatically prefixes them with a package, which confirms the “top-level functions are packages” feature.
-
contrast
In practice, which one should I choose between Object, Companion Object, and top-level? To make a simple judgment, follow these two principles:
- If you want to write the functionality of the utility class, create the file directly and write the top-level function.
- If you need to inherit from another class or implement an interface, use
object
ζcompanion object
.
constant
So in Java, in addition to the static variables and methods that we talked about above, we use static when we declare constants, so what happens when we declare constants in Kotlin?
-
Declare constants in Java:
β οΈpublic class Sample {π πpublic static final int CONST_NUMBER = 1; } Copy the code
-
Declare constants in Kotlin:
π οΈclass Sample { companion object{π/ / π const val CONST_NUMBER = 1 } } const val CONST_SECOND_NUMBER = 2 Copy the code
Found differences include:
- Kotlin’s constants must be declared in an object (including companion objects) or “top-level” because constants are static.
- Kotlin added modifiers for constants
const
The keyword.
And there’s another difference:
- Only basic and String types can be declared constants in Kotlin.
The reason for this is that constant in Kotlin refers to “compile-time constant,” which means that “the compiler knows at compile time what the actual value of this thing is at each call,” so it can be hard-coded directly into the code where it is used at compile time.
Instead of basic and String variables, you can change the value inside an object by calling its methods or variables, so that the variable is not constant. Let’s take a look at a Java example, such as a User class:
β οΈpublic class User {
int id; // π Can be modified
String name; // π Can be modified
public User(int id, String name) {
this.id = id;
this.name = name; }}Copy the code
Declare a static final User instance where it is used, which cannot be assigned twice:
β οΈstatic final User user = new User(123."Zhangsan");
π π
Copy the code
However, you can change the value of the user instance by accessing its member variables:
β οΈ user name ="Lisi";
π
Copy the code
So a constant in Java can be considered “pseudo-constant” because its internal value can be changed in this way. Kotlin’s constants don’t have this problem because they have to be basic types, so they fit the definition of constants.
The val “read-only” and static variables mentioned above are both for individual variables. Now let’s look at another common topic in programming: arrays and collections.
Arrays and collections
An array of
Declare an array of strings:
-
How to write in Java
βοΈ String[] strs = {"a"."b"."c"}; π π Copy the code
-
Kotlin:
π οΈval strs: Array<String> = arrayOf("a"."b"."c") π π Copy the code
You can see that the array in Kotlin is a class that has a generic type, and the creation function is a generic function, as is the collection data type.
We’ll talk about generics later in this article, but let’s start with Java generics.
What are the benefits of generalizing arrays? Arrays can be more powerful as collections, and Kotlin can add a number of useful utility functions to arrays due to generalization:
get() / set()
contains()
first()
find()
This greatly increases the utility of arrays.
-
Value and Modification
Getting or setting array elements in Kotlin, as in Java, can be indexed using square brackets and subscripts:
π οΈ println (STRS [0]) π π STRS [1] = "B" Copy the code
-
Covariant is not supported
Kotlin’s arrays are still compiled to bytecode using Java arrays, but are generic implementations at the language level. This loses covariance, which means that subclass array objects cannot be assigned to subclass array variables:
-
Kotlin
π οΈval strs: Array<String> = arrayOf("a"."b"."c") π val anys: Array<Any> = strs // compile-error: Type mismatch π Copy the code
-
This is possible in Java:
βοΈ String[] strs = {"a"."b"."c"}; π Object[] objs = strs; // success π Copy the code
I’m not going to expand on covariant here, but I’ll talk about it later when I talk about generics.
-
A collection of
Kotlin, like Java, has three collection types: List, Set, and Map, which mean the following:
List
Stores a set of elements in a fixed order that can be repeated.Set
Stores a set of unequal elements, usually in no fixed order.Map
A data set that stores key-value pairs where the keys are not equal, but different keys can correspond to the same value.
How has the use of these three collection types changed from Java to Kotlin? Let’s take a look.
-
List
-
Create a list in Java:
β οΈ a List < String > strList =new ArrayList<>(); strList.add("a"); strList.add("b"); strList.add("c"); // π Adding elements is cumbersome Copy the code
-
Create a list in Kotlin:
π οΈval strList = listOf("a"."b"."c") Copy the code
The first thing you can see is that creating a List in Kotlin is extraordinarily simple, kind of like creating an array. And the List in Kotlin has one more feature: support covariant (covariant). In other words, we can assign the List of a subclass to the List variable of a superclass:
-
Kotlin:
π οΈval strs: List<String> = listOf("a"."b"."c") π val anys: List<Any> = strs // success π Copy the code
-
In Java, this would cause an error:
β οΈ a List < String > strList =new ArrayList<>(); π List<Object> objList = strList; // π compile error: incompatible types π Copy the code
For covariant support or not, lists and arrays are reversed. As for covariation, we need to take a brief look at it with examples, which will be discussed in later articles.
-
And array
The API for Kotlin arrays and MutableList are very similar. The main difference is that the number of elements in an array cannot be changed. So when do we use arrays?
-
This problem exists in Java. Arrays and lists have similar functions, but lists have more functions, so the intuition is to use List. Arrays are not without their advantages, however. Arrays of basic types (int[], float[]) perform better without autoboxing.
-
The same is true in Kotlin, where arrays are preferable in situations where performance requirements are more demanding and the element type is a basic type. One thing to note here, however, is that Kotlin needs to use a specialized primitive array class (IntArray FloatArray LongArray) to avoid packing. In other words, when the elements are not of a basic type, it’s easier to use List than Array.
-
-
-
Set
-
Create a Set in Java:
β οΈ Set < String > strSet =new HashSet<>(); strSet.add("a"); strSet.add("b"); strSet.add("c"); Copy the code
-
Create the same Set in Kotlin:
π οΈval strSet = setOf("a"."b"."c") Copy the code
Similar to the List, the Set is also covariant.
-
-
Map
-
Create a Map in Java:
βοΈ Map<String, Integer> map = new HashMap<>(); map.put("key1".1); map.put("key2".2); map.put("key3".3); map.put("key4".3); Copy the code
-
Create a Map in Kotlin:
π οΈval map = mapOf("key1" to 1."key2" to 2."key3" to 3."key4" to 3) Copy the code
Similar to the two collection types above, the creation code is simple. Each parameter of mapOf represents a key-value pair, and to represents the association of “key” with “value.” This is called an “infix expression,” which I will not expand here until later in this article.
-
Value and Modification
-
In addition to getting a value based on the key using get(), as in Java, Kotlin maps can also be obtained using square brackets:
π οΈ πval value1 = map.get("key1") π val value2 = map["key2"] Copy the code
-
Similarly, Kotlin can use square brackets to change the value of a Map key:
π οΈ πval map = mutableMapOf("key1" to 1."key2" to 2) π map. Put ("key1".2) π map ["key1"] = 2 Copy the code
This uses the knowledge of operator overloading and implements the same Positional Access Operations as arrays, a concept I won’t expand on here until later.
-
-
-
Mutable sets/immutable sets
In the previous example of modifying the Map value, the function was created using mutableMapOf() instead of mapOf(), because only maps created by mutableMapOf() can be modified. There are two types of collections in Kotlin: read-only and mutable. Read only here has two meanings:
- The size of the set is immutable
- The values of the elements in the collection are immutable
Here are three examples of creating immutable and mutable instances of the three collection types:
listOf()
Create immutableList
.mutableListOf()
Create mutableList
.setOf()
Create immutableSet
.mutableSetOf()
Create mutableSet
.mapOf()
Create immutableMap
.mutableMapOf()
Create mutableMap
.
We can see that functions that are mutable create collections that are mutable. Functions that are not mutbale create collections that are mutable, but that are mutable can be converted toMutable by using the toMutable*() function:
π οΈval strList = listOf("a"."b"."c") π strList. ToMutableList ()val strSet = setOf("a"."b"."c") π strSet. ToMutableSet ()val map = mapOf("key1" to 1."key2" to 2."key3" to 3."key4" to 3) π map. ToMutableMap ()Copy the code
Then you can make changes to the collection. Here’s a caveat:
toMutable*()
Returns a new set. The original set is still immutable, so only the set returned by the function can be modified.
Sequence
In addition to collections, Kotlin also introduced a new container type, Sequence, which, like Iterable, can be used to traverse a set of data and do specific processing for each element. Let’s first look at creating a Sequence.
- create
-
Like listOf(), created with a set of elements:
π οΈ sequenceOf ("a"."b"."c") Copy the code
-
Use Iterable to create:
π οΈval list = listOf("a"."b"."c") list.asSequence() Copy the code
List implements the Iterable interface.
-
Use a lambda expression to create:
π οΈ// π The first element val sequence = generateSequence(0) { it + 1 } // π Lambda expression, responsible for generating the second and subsequent elements, with it representing the previous element Copy the code
-
This looks the same as Iterable, so why bother using Sequence? Examples will be used in the next article.
Visibility modifier
Now that we’re done with data sets, let’s look at visibility modifiers in Kotlin. There are four kinds of visibility modifiers in Kotlin:
public
: Public, maximum visibility, can be referenced anywhere.private
: private, minimal visibility, according to the declaration location can be classified as visible in the class and file visible.protected
Protect, protect, protect, protectprivate
+ subclass visible.internal
: Internal, visible only to modules.
Java has an internal “module visible” modifier instead of a default “package visible” modifier. This section discusses the four visibility modifiers in Kotlin and the differences between Kotlin and Java, using examples. Let’s start with public:
public
If you do not write a visibility modifier in Java, you can refer to it only within the same package:
β οΈ πpackage org.kotlinmaster.library;
// There is no visibility modifier
class User {}Copy the code
β οΈ// π is the same package as above
package org.kotlinmaster.library;
public class Example {
void method(a) {
new User(); // success}}Copy the code
β οΈpackage org.kotlinmaster;
// π is not a package
import org.kotlinmaster.library.User;
π
public class OtherPackageExample {
void method(a) {
new User(); // compile-error: 'org.kotlinmaster.library.User' is not public in 'org.kotlinmaster.library'. Cannot be accessed from outside package}}Copy the code
If you want a reference outside of package, you need to add the visibility modifier public before class to indicate exposure.
If you don’t write the visibility modifier in Kotlin, it means public, which has the same effect as the public modifier in Java. In Kotlin the public modifier “can be added, but not necessary”.
@hide
In the official Android SDK, there are some methods that are only visible to the SDK and not open to users (because these methods are not stable and will most likely be changed or removed in a later version). To implement this feature, a Javadoc method @hide is added to the method annotation to restrict client access:
β οΈ/ * * *@hideπ * /
public void hideMethod(a) {... }Copy the code
However, this restriction is not strict, and the restricted method can be accessed through reflection. For this, Kotlin introduced a more stringent visibility modifier: internal.
internal
Internal means that decorated classes and functions are visible only within a module. A module is a set of kotlin files that are compiled together.
- Module in Android Studio
- Maven project
This is often the case with Modules in Android Studio, but the Maven project is just a matter of understanding.
Internal is useful when writing a Library Module. When you want to create a function that is only open to use within the Module and not visible to users of the library, you should use the internal visibility modifier.
What happened to Java’s “visible inside the package”?
Java’s default “visible within the package” is deprecated in Kotlin, where the closest visibility modifier to it is the internal “visible within the module.” Why would it be deprecated to switch inside the visible? I think there are several reasons:
- Kotlin encourages the creation of top-level functions and attributes, and a single source file can contain multiple classes, making Kotlin’s source code structure more flat and making the package structure less important than in Java.
- For the sake of decoupling and maintainability of the code, more and more modules become smaller and smaller, so that
internal
“Visible within the Module” already satisfies the need for code encapsulation.
protected
- In Java
protected
Package visible + subclass visible. - In the Kotlin
protected
saidprivate
+ subclass visible.
Kotlin has a narrower scope of visibility than Java protected because Kotlin no longer has the concept of “visibility inside packages” and is more concerned with Modules than Java’s focus on packages.
private
- In Java
private
Represents visible in a class, and is “visible” to external classes when it is an inner class. - In the Kotlin
private
Indicates that the class is visible in or in the file in which it is located, and is “invisible” to external classes when it is an inner class.
Private modified variables “visible in class” and “visible in file” :
π οΈclass Sample {
private val propertyInClass = 1 // π Is available only in the Sample class
}
private val propertyInFile = "A string." // π range is larger and the entire file is visible
Copy the code
The difference between Java and Kotlin when decorating variables of inner classes:
-
In Java, an external class can access the private variables of an inner class:
β οΈpublic class Outter { public void method(a) { Inner inner = new Inner(); π int result = inner.number * 2; // success } private class Inner { private int number = 0; }}Copy the code
-
In Kotlin, an outer class cannot access a private variable of an inner class:
π οΈclass Outter { fun method(a) { valπ inner = inner ()val result = inner.number * 2 // compile-error: Cannot access 'number': it is private in 'Inner' } class Inner { private val number = 1}}Copy the code
-
You can decorate classes and interfaces
-
In Java, only one external class is allowed per file, so classes and interfaces are not allowed to be set to private, because declaring private cannot be used externally, which makes no sense.
-
Kotlin allows multiple class and top-level functions and properties to be declared in the same file, so Kotlin allows classes and interfaces to be declared private because other members of the same file can access them:
π οΈprivate class Sample { val number = 1 fun method(a) { println("Sample method()")}}// π is in the same file, so it is accessible val sample = Sample() Copy the code
-
exercises
- Create a Kotlin class that disallows outsiders from creating instances through the constructor and provides at least one way to instantiate them.
- Use Array, IntArray, List to achieve “save 1-100_000 numbers, and calculate the average of these numbers”, and print out the execution time of these three data structures.
The authors introduce
Video author
Throw line (Zhu Kai)
- Code on the school founder, project manager, content module planner and video content author.
- Android GDE (Google Certified Android Development Specialist), former Flipboard Android Engineer.
- GitHub is ranked 92nd in the world for Java and has 6.6 K followers and 9.9 K stars on GitHub.
- The personal Android open source library, MaterialEditText, is referenced by several projects around the world, including Flipboard, a news-reading software with 500 million users worldwide.
- Served as the lecturer of Android in Google Developer Group Beijing offline sharing sessions for many times.
- After the personal technical article “RxJava Details for Android Developers” was released, it was forwarded and shared within many companies and teams in China and served as the main source of information for technical meetings of teams. It was also backtransmitted to some Chinese teams in some Companies such as Google and Uber in the United States.
- HenCoder, an advanced Android teaching website founded, enjoys considerable influence in the Global Chinese Android development community.
- After that, he founded The Android advanced development teaching course HenCoder Plus, with students from all over the world, including senior software engineers from Taiwan, Japan, the United States and other regions, including Ali, Tiaotiao, Huawei, Tencent and other famous first-tier Internet companies.
The authors
Walker Zhang Lei
Walker zhang Lei is a senior engineer at Jike Android. In 2015, I joined Jike and participated in the architecture design and product iteration of Jike 2.0 to 6.0. Years of Experience in Android development, worked for OPPO, focusing on client user experience, audio and video development and performance optimization.