目次
- 序文
- 1. Kotlin環境構築
- 2、オブジェクト
- 3、クラス
- 4. 継承
- 5. 変数
- 6. 定数
- 7. 静的定数
- 8. 定義方法
- 九、オーバーロード法
- 10. 基本的なデータ型
- 11. 比較タイプ
- 12. コンバーター
- 13、文字列比較
- 14. アレイ
- 15. サイクル
- 16. コーナーマークサイクル
- 17、アドバンストサイクル
- 18. 裁判官
- 19. コンストラクター
- 二十、クラス作成
- 二十一、民営化セット方式
- 二十二、民営化取得法
- 23. 列挙
- 24. インターフェース
- 25. 匿名の内部クラス
- 26、インナークラス
- 27、内部クラスは外部クラスと同じ名前の変数にアクセスします。
- 28、抽象クラス
- 29、静的変数とメソッド
- 30 個の可変パラメータ
- 31、ジェネリック
- 32、コードブロックの構築
- 33、静的コード ブロック
- 35、メソッドコードブロック
- 36、可視修飾子
- 37、findViewById は必要ありません
- 38、ラムダ
- 39、関数変数
- 40. 航空安全
- 41. このメソッドはデフォルトパラメータの追加をサポートしています
- 42、クラスメソッド拡張
- 43、演算子のオーバーロード
- 四十五、拡張機能
- 46、コルーチン
- 要約する
序文
Google I/O 2017 で、Google は Kotlin を公式の Android 開発言語として発表しました。Kotlin は、Java 仮想マシン上で実行される静的型付けプログラミング言語であり、Java バイトコードまたは JavaScript にコンパイルでき、Java 環境で完全に実行できます。
Kotlin 言語には主に次の特徴があります。
1. シンプルかつ高度な構文 2.
Null ポインターの安全性
3. Java との完全な互換性: Kotlin と Java を混合して開発可能
4. 関数型プログラミング: 拡張関数、ラムダ式のサポート
5. 型推論: 型を指定する必要がない
6. . 強力なコルーチン: スレッド問題の処理
7. デリゲート: 遅延読み込み、読み込み速度の向上
8. 不変データ
9. コントロールを取得するために findViewById を必要としない: 一部の場所は推奨されません
Kotlinはみんなの力の集合体とも言え、Androidネイティブコードの開発効率を大幅に向上させ、非常に人気があります。
1. Kotlin環境構築
プロジェクトを作成するときに、[Kotlin サポートを含める] オプションを直接オンにして、Kotlin 言語を選択します。
古い Java プロジェクトの場合、Android Studio は Kotlin プラグインをインストールし、同時に手動で Gradle を構成する必要があります。構成は次のとおりです。
アプリ モジュールの gradle の下に次のコードを追加します。
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
プロジェクト モジュールの gradle の下に次のコードを追加します。
ext.kotlin_version = '1.4.21'
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
以下では、Kotlin 言語の具体的な紹介と使い方を、わかりやすく Java 言語と比較しながら説明します。
2、オブジェクト
Javaの書き方
MainActivity.this
Kotlin で書く
this@MainActivity
3、クラス
Javaの書き方
MainActivity.class
Kotlin で書く
MainActivity::class.java
4. 継承
Javaの書き方
public class MainActivity extends AppCompatActivity {
}
Kotlin での作成 (Kotlin の継承クラスは open キーワードを使用して変更する必要があります)
class MainActivity : AppCompatActivity() {
}
5. 変数
Javaの書き方
Intent intent = new Intent();
Kotlin で書く
var intent = Intent()
6. 定数
Javaの書き方
final String text = "";
Kotlin で書く
val text = ""
7. 静的定数
Javaの書き方
public class MainActivity extends AppCompatActivity {
static final String text = "";
}
Kotlinの書き方(静的変数はクラスの上に定義する必要があるので注意)
const val text = ""
class MainActivity : AppCompatActivity() {
}
8. 定義方法
Javaの書き方
public void test(String message) {
}
Kotlinの書き方(Unitはvoidと同じ効果)
fun test(message : String) : Unit {
}
// 在 Kotlin 可以省略 Unit 这种返回值
fun test(message : String) {
}
九、オーバーロード法
Javaの書き方
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
Kotlin で書く
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
10. 基本的なデータ型
Javaの書き方
int i = 1;
long l = 2;
boolean b = true;
float f = 0;
double d = 0;
char c = 'A';
String s = "text";
Kotlin で書く
var i : Int = 1
var l : Long = 2
var b : Boolean = true
var f : Float = 0F
var d : Double = 0.0
var c : Char = 'A'
var s : String = "text"
// 更简洁点可以这样,自动推倒类型
var i = 1
var l = 2
var b = true
var f = 0F
var d = 0.0
var c = 'A'
var s = "text"
11. 比較タイプ
Javaの書き方
if ("" instanceof String) {
}
Kotlin で書く
if ("" is String) {
}
12. コンバーター
Javaの書き方
int number = 100;
System.out.println(String.format("数量有%d", number));
Kotlin で書く
var number = 100
println("数量有${
number}")
// 换种简洁的写法
var number = 100
println("数量有$number")
// 如果不想字符串被转义可以使用\$
var number = 100
println("数量有\$number")
13、文字列比較
Javaの書き方
String s1 = "text";
String s2 = "text";
if (s1.equals(s2)) {
}
Kotlin の記述方法 (Kotlin は文字列比較の記述方法を最適化します。他の種類のオブジェクト比較では引き続き equals メソッドを使用する必要があります)
var s1 = "text"
var s2 = "text"
if (s1 == s2) {
}
14. アレイ
Javaの書き方
int[] array1 = {
1, 2, 3};
float[] array2 = {
1f, 2f, 3f};
String[] array3 = {
"1", "2", "3"};
Kotlin で書く
val array1 = intArrayOf(1, 2, 3)
val array2 = floatArrayOf(1f, 2f, 3f)
val array3 = arrayListOf("1", "2", "3")
15. サイクル
Javaの書き方
String[] array = {
"1", "2", "3"};
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
Kotlin で書く
val array = arrayListOf("1", "2", "3")
for (i in array.indices) {
println(array[i])
}
16. コーナーマークサイクル
Javaの書き方
String[] array = {
"1", "2", "3"};
for (int i = 1; i < array.length; i++) {
System.out.println(array[i]);
}
Kotlinの書き方(Kotlinではこの書き方をインターバルと呼びます)
val array = arrayListOf("1", "2", "3")
for (i in IntRange(1, array.size - 1)) {
println(array[i])
}
// 换种更简洁的写法
val array = arrayListOf("1", "2", "3")
for (i in 1..array.size - 1) {
println(array[i])
}
// 编译器提示要我们换种写法
val array = arrayListOf("1", "2", "3")
for (i in 1 until array.size) {
println(array[i])
}
17、アドバンストサイクル
Javaの書き方
String[] array = {
"1", "2", "3"};
for (String text : array) {
System.out.println(text);
}
Kotlin で書く
val array = arrayListOf("1", "2", "3")
for (text in array) {
println(text)
}
18. 裁判官
Javaの書き方
int count = 1;
switch (count) {
case 0:
System.out.println(count);
break;
case 1:
case 2:
System.out.println(count);
break;
default:
System.out.println(count);
break;
}
在这里插入代码片
Kotlin で書く
var count = 1
when (count) {
0 -> {
println(count)
}
in 1..2 -> {
println(count)
}
else -> {
println(count)
}
}
var count = 1
// 换种更简洁的写法
when (count) {
0 -> println(count)
in 1..2 -> println(count)
else -> println(count)
}
19. コンストラクター
Javaの書き方
public class MyView extends View {
public MyView(Context context) {
this(context, null);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
Kotlin で書く
class MyView : View {
constructor(context : Context) : this(context, null) {
}
constructor(context : Context, attrs : AttributeSet?) : this(context, attrs, 0) {
}
constructor(context : Context, attrs : AttributeSet?, defStyleAttr : Int) : super(context, attrs, defStyleAttr) {
}
}
// 换种更简洁的写法
class MyView : View {
constructor(context : Context) : this(context, null)
constructor(context : Context, attrs : AttributeSet?) : this(context, attrs, 0)
constructor(context : Context, attrs : AttributeSet?, defStyleAttr : Int) : super(context, attrs, defStyleAttr)
}
// 只有一种构造函数的还可以这样写
class MyView(context: Context?) : View(context) {
}
二十、クラス作成
Javaの書き方
public class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Person person = new Person("Zhangsan", 100);
person.setName("ZS");
person.setAge(18);
System.out.println("name: " + person.getName() + ", age: " + person.getAge());
Kotlinの書き方(メンバー変数のsetメソッドを公開したくない場合はvarをvalに変更すればOK)
class Person {
var name : String? = null
get() = field
set(value) {
field = value}
var age : Int = 0
get() = field
set(value) {
field = value}
}
// 换种更简洁的写法
class Person(var name : String, var age : Int)
var person = Person("Zhangsan", 100)
person.name = "ZS"
person.age = 18
println("name: {
$person.name}, age: {
$person.age}")
二十一、民営化セット方式
Javaの書き方
public class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
private void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
private void setAge(int age) {
this.age = age;
}
}
Kotlin で書く
class Person {
var name : String? = null
private set
var age : Int = 0
private set
}
二十二、民営化取得法
Javaの書き方
public class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private String getName() {
return name;
}
private void setName(String name) {
this.name = name;
}
private int getAge() {
return age;
}
private void setAge(int age) {
this.age = age;
}
}
Kotlin で書く
class Person {
private var name : String? = null
private var age : Int = 0
}
23. 列挙
Javaの書き方
enum Sex {
MAN(true), WOMAN(false);
Sex(boolean isMan) {
}
}
Kotlin で書く
enum class Sex (var isMan: Boolean) {
MAN(true), WOMAN(false)
}
24. インターフェース
Javaの書き方
public interface Callback {
void onSuccess();
void onFail();
}
Kotlinの記述方法(Kotlinインターフェースのメソッドは自前で実装できるため、ここでは説明しません)
interface Callback {
fun onSuccess()
fun onFail()
}
25. 匿名の内部クラス
Javaの書き方
new Callback() {
@Override
public void onSuccess() {
}
@Override
public void onFail() {
}
};
Kotlin で書く
object:Callback {
override fun onSuccess() {
}
override fun onFail() {
}
}
26、インナークラス
Javaの書き方
public class MainActivity extends AppCompatActivity {
public class MyTask {
}
}
Kotlin で書く
class MainActivity : AppCompatActivity() {
inner class MyTask {
}
}
27、内部クラスは外部クラスと同じ名前の変数にアクセスします。
Javaの書き方
String name = "Zhangsan";
public class MyTask {
String name = "ZS";
public void show() {
System.out.println(name + "---" + MainActivity.this.name);
}
}
Kotlin で書く
var name = "Zhangsan"
inner class MyTask {
var name = "ZS"
fun show() {
println(name + "---" + this@MainActivity.name)
}
}
28、抽象クラス
Javaの書き方
public abstract class BaseActivity extends AppCompatActivity implements Runnable {
abstract void init();
}
Kotlin で書く
abstract class BaseActivity : AppCompatActivity(), Runnable {
abstract fun init()
}
29、静的変数とメソッド
Javaの書き方
public class ToastUtils {
public static Toast sToast;
public static void show() {
sToast.show();
}
}
Kotlinの書き方(Kotlinではこの書き方をコンパニオンオブジェクトと呼びます)
companion object ToastUtils {
var sToast : Toast? = null
fun show() {
sToast!!.show()
}
}
30 個の可変パラメータ
Javaの書き方
public int add(int... array) {
int count = 0;
for (int i : array) {
count += i;
}
return count;
}
Kotlin で書く
fun add(vararg array: Int) : Int {
var count = 0
//for (i in array) {
// count += i
//}
array.forEach {
count += it
}
return count
}
31、ジェネリック
Javaの書き方
public class Bean<T extends String> {
T data;
public Bean(T t) {
this.data = t;
}
}
Bean<String> bean = new Bean<>("666666");
Kotlin で書く
class Bean<T : Comparable<String>>(t: T) {
var data = t
}
var bean = Bean<String>("666666")
// 换种更简洁的写法
var bean = Bean("666666")
32、コードブロックの構築
Javaの書き方
public class MainActivity extends AppCompatActivity {
int number;
{
number = 1;
}
}
Kotlin で書く
class MainActivity : AppCompatActivity() {
var number = 0
init {
number = 1
}
}
33、静的コード ブロック
Javaの書き方
public class MainActivity extends AppCompatActivity {
static int number;
static {
number = 1;
}
}
Kotlin で書く
class MainActivity : AppCompatActivity() {
companion object {
var number = 0
init {
number = 1
}
}
}
35、メソッドコードブロック
Javaの書き方
void test(){
{
int a = 1;
}
}
Kotlin で書く
fun test() {
run {
var a =1
}
}
36、可視修飾子
Javaでの書き方(デフォルトはデフォルト)
修飾子 | 効果 |
---|---|
公共 | すべてのクラスが表示されます |
保護された | サブクラスが表示されます |
デフォルト | 同じパッケージ内のクラスが表示されます |
プライベート | 自分のクラスにのみ表示されます |
Kotlinの書き方(デフォルトはpublic)
修飾子 | 効果 |
---|---|
公共 | すべてのクラスが表示されます |
内部 | 同じモジュール内のクラスが表示されます |
保護された | サブクラスが表示されます |
プライベート | 自分のクラスにのみ表示されます |
37、findViewById は必要ありません
レイアウトで定義される
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
TextViewのテキストを直接設定する
tv_content.text = "改变文本"
38、ラムダ
Java JDKではLambda式が追加されましたが、普及には至っていないため、現在はKotlinを使用するのが良いでしょう。
tv_content.setOnClickListener(View.OnClickListener(
fun(v : View) {
v.visibility = View.GONE
}
))
ラムダ式で簡略化が可能になりました
tv_content.setOnClickListener {
v -> v.visibility = View.GONE }
39、関数変数
関数は Kotlin 構文の変数として渡すことができます
var result = fun(number1 : Int, number2 : Int) : Int {
return number1 + number2
}
この関数変数を使用します
println(result(1, 2))
40. 航空安全
Java では空のオブジェクトの処理を強制されないため、NullPointerException null ポインターが頻繁に発生します。現在、Kotlin では空のオブジェクトが制限されており、コンパイル時にオブジェクトが空かどうかを処理する必要があり、そうでない場合はコンパイルが通過しません。 。
null 以外のオブジェクトの場合は、このオブジェクトを直接使用できます。
fun getText() : String {
return "text"
}
val text = getText()
print(text.length)
null 許容オブジェクトの場合、オブジェクトが空かどうかを判断する必要があります。
fun getText() : String? {
return null
}
val text = getText()
if (text != null) {
print(text.length)
}
// 如果不想判断是否为空,可以直接这样,如果 text 对象为空,则会报空指针异常,一般情况下不推荐这样使用
val text = getText()
print(text!!.length)
// 还有一种更好的处理方式,如果 text 对象为空则不会报错,但是 text.length 的结果会等于 null
val text = getText()
print(text?.length)
41. このメソッドはデフォルトパラメータの追加をサポートしています
Java では、メソッドを拡張するために複数のオーバーロードがある場合があります。
public void toast(String text) {
toast(this, text, Toast.LENGTH_SHORT);
}
public void toast(Context context, String text) {
toast(context, text, Toast.LENGTH_SHORT);
}
public void toast(Context context, String text, int time) {
Toast.makeText(context, text, time).show();
}
toast("弹个窗");
toast(this, "弹个窗");
toast(this, "弹个窗", Toast.LENGTH_LONG);
しかし、Kotlin ではオーバーロードする必要はなく、メソッド上でパラメータのデフォルト値を直接定義できます。
fun toast(context : Context = this, text : String, time : Int = Toast.LENGTH_SHORT) {
Toast.makeText(context, text, time).show()
}
toast(text = "弹个窗")
toast(this, "弹个窗")
toast(this, "弹个窗", Toast.LENGTH_LONG)
42、クラスメソッド拡張
String クラスのメソッドを拡張するなど、継承せずに元のクラスのメソッドを拡張できます。
fun String.handle() : String {
return this + "Zhangsan"
}
// 需要注意,handle 方法在哪个类中被定义,这种扩展只能在那个类里面才能使用
print("ZS = ".handle())
ZS = ZhangSan
43、演算子のオーバーロード
Kotlin で演算子を使用すると、最終的にオブジェクトの対応するメソッドが呼び出されます。これらのメソッドを書き換えることで、このオブジェクトに演算子をサポートさせることができます。
オペレーター | 呼び出しメソッド |
---|---|
+α | a.unaryPlus() |
-a | a.unaryMinus() |
!a | a.not() |
オペレーター | 呼び出しメソッド |
---|---|
a++ | a.inc() |
はー | a.dec() |
オペレーター | 呼び出しメソッド |
---|---|
a + b | a.プラス(b) |
a - b | a.マイナス(b) |
a * b | a×(b) |
a / b | a.div(b) |
a%b | a.rem(b)、a.mod(b) (非推奨) |
a…b | a.rangeTo(b) |
オペレーター | 呼び出しメソッド |
---|---|
bのa | b.(a)を含む |
!in b | !b.contains(a) |
オペレーター | 呼び出しメソッド |
---|---|
a[i] | a.get(i) |
a[i, j] | a.get(i, j) |
a[i_1, …, i_n] | a.get(i_1, …, i_n) |
a[i] = b | a.set(i, b) |
a[i, j] = b | a.set(i, j, b) |
a[i_1, …, i_n] = b | a.set(i_1, …, i_n, b) |
オペレーター | 呼び出しメソッド |
---|---|
あ() | a.invoke() |
a(i) | a.invoke(i) |
a(i, j) | a.invoke(i, j) |
a(i_1, …, i_n) | a.invoke(i_1, …, i_n) |
オペレーター | 呼び出しメソッド |
---|---|
a += b | a.plusAssign(b) |
a -= b | a.minusAssign(b) |
a *= b | a.timesAssign(b) |
a /= b | a.divAssign(b) |
a %= b | a.remAssign(b)、a.modAssign(b) (非推奨) |
オペレーター | 呼び出しメソッド |
---|---|
a == b | a?.equals(b) ?: (b === null) |
a != b | !(a?.equals(b) ?: (b === null)) |
オペレーター | 呼び出しメソッド |
---|---|
a > b | a.compareTo(b) > 0 |
a < b | a.compareTo(b) < 0 |
a >= b | a.compareTo(b) >= 0 |
a <= b | a.compareTo(b) <= 0 |
四十五、扩展函数
扩展函数是 Kotlin 用于简化一些代码的书写产生的,其中有 let、with、run、apply、also 五个函数
1、let 函数
在函数块内可以通过 it 指代该对象。返回值为函数块的最后一行或指定return表达式
一般写法
fun main() {
val text = "CurvedBowZhang"
println(text.length)
val result = 1000
println(result)
}
let 写法
fun main() {
val result = "CurvedBowZhang".let {
println(it.length)
1000
}
println(result)
}
最常用的场景就是使用let函数处理需要针对一个可null的对象统一做判空处理
mVideoPlayer?.setVideoView(activity.course_video_view)
mVideoPlayer?.setControllerView(activity.course_video_controller_view)
mVideoPlayer?.setCurtainView(activity.course_video_curtain_view)
mVideoPlayer?.let {
it.setVideoView(activity.course_video_view)
it.setControllerView(activity.course_video_controller_view)
it.setCurtainView(activity.course_video_curtain_view)
}
又或者是需要去明确一个变量所处特定的作用域范围内可以使用
2、with 函数
前面的几个函数使用方式略有不同,因为它不是以扩展的形式存在的。它是将某对象作为函数的参数,在函数块内可以通过 this 指代该对象。返回值为函数块的最后一行或指定return表达式
定义 Person 类
class Person(var name : String, var age : Int)
一般写法
fun main() {
var person = Person("CurvedBowZhang", 100)
println(person.name + person.age)
var result = 1000
println(result)
}
with 写法
fun main() {
var result = with(Person("CurvedBowZhang", 100)) {
println(name + age)
1000
}
println(result)
}
适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于 Android 中RecyclerView中onBinderViewHolder中,数据 model 的属性映射到 UI 上
override fun onBindViewHolder(holder: ViewHolder, position: Int){
val item = getItem(position)?: return
holder.nameView.text = "姓名:${
item.name}"
holder.ageView.text = "年龄:${
item.age}"
}
override fun onBindViewHolder(holder: ViewHolder, position: Int){
val item = getItem(position)?: return
with(item){
holder.nameView.text = "姓名:$name"
holder.ageView.text = "年龄:$age"
}
}
3、run 函数
实际上可以说是let和with两个函数的结合体,run函数只接收一个lambda函数为参数,以闭包形式返回,返回值为最后一行的值或者指定的return的表达式
一般写法
var person = Person("CurvedBowZhang", 100)
println(person.name + "+" + person.age)
var result = 1000
println(result)
run 写法
var person = Person("CurvedBowZhang", 100)
var result = person.run {
println("$name + $age")
1000
}
println(result)
适用于let,with函数任何场景。
因为run函数是let,with两个函数结合体,准确来说它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法;
另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理,这里还是借助 onBindViewHolder 案例进行简化
override fun onBindViewHolder(holder: ViewHolder, position: Int){
val item = getItem(position)?: return
holder.nameView.text = "姓名:${
item.name}"
holder.ageView.text = "年龄:${
item.age}"
}
override fun onBindViewHolder(holder: ViewHolder, position: Int){
val item = getItem(position)?: return
item?.run {
holder.nameView.text = "姓名:$name"
holder.ageView.text = "年龄:$age"
}
}
4、apply 函数
从结构上来看apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,run函数是以闭包形式返回最后一行代码的值,而apply函数的返回的是传入对象的本身
一般写法
val person = Person("CurvedBowZhang", 100)
person.name = "ZJX"
person.age = 50
apply 写法
val person = Person("CurvedBowZhang", 100).apply {
name = "ZJX"
age = 50
}
整体作用功能和run函数很像,唯一不同点就是它返回的值是对象本身,而run函数是一个闭包形式返回,返回的是最后一行的值。正是基于这一点差异它的适用场景稍微与run函数有点不一样。
apply一般用于一个对象实例初始化的时候,需要对对象中的属性进行赋值。或者动态inflate出一个XML的View的时候需要给View绑定数据也会用到,这种情景非常常见。特别是在我们开发中会有一些数据model向View model转化实例化的过程中需要用到
mRootView = View.inflate(activity, R.layout.example_view, null)
mRootView.tv_cancel.paint.isFakeBoldText = true
mRootView.tv_confirm.paint.isFakeBoldText = true
mRootView.seek_bar.max = 10
mRootView.seek_bar.progress = 0
使用 apply 函数后的代码是这样的
mRootView = View.inflate(activity, R.layout.example_view, null).apply {
tv_cancel.paint.isFakeBoldText = true
tv_confirm.paint.isFakeBoldText = true
seek_bar.max = 10
seek_bar.progress = 0
}
多层级判空问题
if (mSectionMetaData == null || mSectionMetaData.questionnaire == null || mSectionMetaData.section == null) {
return;
}
if (mSectionMetaData.questionnaire.userProject != null) {
renderAnalysis();
return;
}
if (mSectionMetaData.section != null && !mSectionMetaData.section.sectionArticles.isEmpty()) {
fetchQuestionData();
return;
}
kotlin的apply函数优化
mSectionMetaData?.apply {
//mSectionMetaData不为空的时候操作mSectionMetaData
}?.questionnaire?.apply {
//questionnaire不为空的时候操作questionnaire
}?.section?.apply {
//section不为空的时候操作section
}?.sectionArticle?.apply {
//sectionArticle不为空的时候操作sectionArticle
}
5、also 函数
also函数的结构实际上和let很像唯一的区别就是返回值的不一样,let是以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。而also函数返回的则是传入对象的本身
fun main() {
val result = "CurvedBowZhang".let {
println(it.length)
1000
}
println(result) // 打印:1000
}
fun main() {
val result = "CurvedBowZhang".also {
println(it.length)
}
println(result) // 打印:CurvedBowZhang
}
适用于let函数的任何场景,also函数和let很像,只是唯一的不同点就是let函数最后的返回值是最后一行的返回值而also函数的返回值是返回当前的这个对象。一般可用于多个扩展函数链式调用
6、小结
通过以上几种函数的介绍,可以很方便优化 Kotlin 中代码编写,整体看起来几个函数的作用很相似,但是各自又存在着不同。使用的场景也有相同的地方,比如:run函数就是let和with的结合体。
一张图说明:
四十六、协程
子任务协作运行,优雅的处理异步问题解决方案
协程实际上就是极大程度的复用线程,通过让线程满载运行,达到最大程度的利用CPU,进而提升应用性能
在当前 app module 中配置环境和依赖(因为现在协程在 Kotlin 中是实验性的)
kotlin {
experimental {
coroutines 'enable'
}
}
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.20'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.20'
}
协程的三种启动方式
runBlocking:T
launch:Job
async/await:Deferred
- runBlocking
runBlocking :运行阻塞。用代码测试一下
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
runBlocking {
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试延迟开始")
delay(20000) // 因为 Activity 最长响应时间为 15 秒
println("测试延迟结束")
}
println("测试结束")
17:02:08.686 System.out: 测试是否为主线程 true
17:02:08.686 System.out: 测试开始
17:02:08.688 System.out: 测试是否为主线程 true
17:02:08.688 System.out: 测试延迟开始
17:02:28.692 System.out: 测试延迟结束
17:02:28.693 System.out: 测试结束
runBlocking 运行在主线程,过程中 App 出现过无响应提示,由此可见 runBlocking 和它的名称一样,真的会阻塞当前的线程,只有等 runBlocking 里面的代码执行完了才会执行 runBlocking 外面的代码
- launch
launch :启动。用代码测试
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
launch {
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试延迟开始")
delay(20000)
println("测试延迟结束")
}
println("测试结束")
17:19:17.190 System.out: 测试是否为主线程 true
17:19:17.190 System.out: 测试开始
17:19:17.202 System.out: 测试结束
17:19:17.203 System.out: 测试是否为主线程 false
17:19:17.203 System.out: 测试延迟开始
17:19:37.223 System.out: 测试延迟结束
- async
async :异步。用代码测试
测试的时候是主线程,但是到了 launch 中就会变成子线程,这种效果类似 new Thread(),和 runBlocking 最不同的是 launch 没有执行顺序这个概念
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
async {
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试延迟开始")
delay(20000)
println("测试延迟结束")
}
println("测试结束")
17:29:00.694 System.out: 测试是否为主线程 true
17:29:00.694 System.out: 测试开始
17:29:00.697 System.out: 测试结束
17:29:00.697 System.out: 测试是否为主线程 false
17:29:00.697 System.out: 测试延迟开始
17:29:20.707 System.out: 测试延迟结束
这结果不是跟 launch 一样么?那么这两个到底有什么区别呢?,让我们先看一段测试代码
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
val async = async {
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试延迟开始")
delay(20000)
println("测试延迟结束")
return@async "666666"
}
println("测试结束")
runBlocking {
println("测试返回值:" + async.await())
}
17:50:57.117 System.out: 测试是否为主线程 true
17:50:57.117 System.out: 测试开始
17:50:57.120 System.out: 测试结束
17:50:57.120 System.out: 测试是否为主线程 false
17:50:57.120 System.out: 测试延迟开始
17:51:17.131 System.out: 测试延迟结束
17:51:17.133 System.out: 测试返回值:666666
这里就说明了 async 和 launch 还是有区别的,async 可以有返回值,通过它的 await 方法进行获取,需要注意的是这个方法只能在协程的操作符中才能调用
- 线程调度
协程也有类似 RxJava 的线程调度,先用 launch 试验一下
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
launch(CommonPool) {
// 同学们,敲重点
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试延迟开始")
delay(20000)
println("测试延迟结束")
}
println("测试结束")
18:00:23.243 System.out: 测试是否为主线程 true
18:00:23.244 System.out: 测试开始
18:00:23.246 System.out: 测试结束
18:00:23.246 System.out: 测试是否为主线程 false
18:00:23.247 System.out: 测试延迟开始
18:00:43.256 System.out: 测试延迟结束
Q:这个跟刚刚的代码有什么不一样吗?
A:当然不一样,假如一个网络请求框架维护了一个线程池,一个图片加载框架也维护了一个线程池…,你会发现其实这样不好的地方在于,这些线程池里面的线程没有被重复利用,于是乎协程主动维护了一个公共的线程池 CommonPool,很好的解决了这个问题
Q:还有刚刚不是说能线程调度吗?为什么还是在子线程运行?
A:因为我刚刚只用了 CommonPool 这个关键字,再介绍另一个关键字 UI,光听名字就知道是啥了
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
launch(UI) {
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试延迟开始")
delay(20000)
println("测试延迟结束")
}
println("测试结束")
18:07:20.181 System.out: 测试是否为主线程 true
18:07:20.181 System.out: 测试开始
18:07:20.186 System.out: 测试结束
18:07:20.192 System.out: 测试是否为主线程 true
18:07:20.192 System.out: 测试延迟开始
18:07:40.214 System.out: 测试延迟结束
总结
以上这些仅仅只是 Kotlin 的一些基本使用,还有很多神奇之处和高级用法,帮助我们更加优雅地实现功能,后续我会继续补充在后面,也方便自己开发查阅。