Kotlin 基础(一)

Kotlin 是一门非常优秀的语言,兼容了 N 多种语言的优点,学习 Kotlin 有助于提升我们多层编程开发的认识。



Kotlin 是静态类型的开源编程语言,可以有效地运行在 Java虚拟机(JVM)上。Kotlin 由 JetBrains 开发,并得到谷歌的强力支持。

一、从类开始

1.1 导包

与 Java 一样,Kotlin 也含有默认导入的特性。Kotlin 的每个文件都默认导入了如下常用包:

-- kotlin.*
-- kotlin.annotation.*
-- kotlin.collections.*
-- kotlin.comparisons.*
-- kotlin.io.*
-- kotlin.ranges.*
-- kotlin.sequences.*
-- kotlin.text.*

Kotlin 的导包方式和 Java 也类似,但有一点,Kotlin 不支持静态导入。Kotlin 为什么不支持静态导入?因为静态导入会增加代码的阅读成本。

1.2 函数声明

fun Kotlin 中用关键字 fun 声明函数。

override fun getRandom(type: String): Observable<JsonResult<List<FuckGoods>>> { return api.getRandom(type) }

Kotlin 为什么使用 fun 来声明函数?为什么不是 function? 或者 def?

JetBrains 团队说: "We use 'fun' because we like it - and yes, we do know what the word means in English."

我们使用 "fun" 因为我们喜欢它,而且我们知道这个词在英语中是啥意思!

"fun" 在英语中是什么意思? 来自谷歌翻译的 fun 的翻译: 动词 开玩笑,名词 玩笑。

1.3 参数

参数声明

Kotlin 的参数使用 Pascal 符号定义,参数之间和 java 类似通过 " , " 分隔,参数需要指明类型。

fun test(count: Int, test2: Int) { }

为什么 Kotlin 像 Pascal 一样,参数声明类型要在参数名后面?count: Int

Rob Pike 曾解释过这个问题: 因为这样更加清晰易懂,当然,我觉得这样存在很大的争议,不过也和工程师自身的习惯有关系。

默认参数

Kotlin 参数可以设置默认值,当需要忽略该参数的时候可以使用参数的默认值。Like this:

off: Int = 0

fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size ) { ... }
空返回值

在 Java 中返回空关键字为 void,在 Kotlin 中的空返回值为 Unit,并且可以省略 .Unit 和 void 不同,Unit 是个真实的类并且是个单例。

// 不省略
fun printHello(name: String?): Unit { ...}
// 省略
fun printHello(name: String?) { ...}

为什么使用Unit?

"Unit" just stands for "something that has only one value", it’s a traditional name, comes from functional languages. I agree that this name is not very intuitive, but we failed to invent a better name.

"Unit" 代表 "只有一个值",它是一个来自函数式语言的传统的名称。我同意这个名字不是很直观,但我们没有想到一个更好的名字。

1.4 局部变量

Kotlin 局部变量分为 val 和 var。

var 关键字声明可变属性,和 Java 变量声明类似;

val 关键字声明只读属性,和 Java 中 final 变量类似,在初始化时需要赋值,以后不能改变。更多的应该使用 val 关键字。

val a: Int = 1 var x = 5

为什么使用 val 关键字?

val 具有 Immutable 或 readonly 的特性,一旦创建,不可改变。没有竞争条件,没有并发问题,无需去同步,保证了线程安全,不需要担心空安全问题。

为什么是 val 和 var?

因为 val 是 value 的缩写,而 var 是 variable。

二、特性

2.1 有趣的冒号

从语法上来看,Kotlin 大量使用了冒号(:)这一符号,可以总结一下,这个冒号在 Kotlin 中究竟代表什么。

考虑下面四种场景:

  • 在变量定义中,代表变量的类型

  • 在类定义中,代表基类的类型

  • 在函数定义中,代表函数返回值的类型

  • 在匿名对象中,代表对象的类型

2.2 字符串模板

Kotlin 中允许字符串中包含 "$" 开头嵌入表达式。

在 Java 中我们可能需要这样定义来拼接字符串:

String message = "n = " + n;

但是在 Kotlin 中,我们可以直接使用 "$" 拼接":

val message = "n = $n"

很显然,使用字符串模板,可以提高我们的开发效率。

2.3 条件表达式

这是一个普通的条件判断语句。

fun maxOf(a: Int, b: Int): Int {
    if (a > b) {
        return a    
    } else {
        return b
    }
}

在 Kotlin 中,if 表达式具有返回值,故可以表述成如下方式:

fun maxOf(a: Int, b: Int) = if (a > b) a else b

2.4 关于空安全

空安全性是 Kotlin 的一大特色,在 Java 中,NPE (NullPointerException) 是常客,但是在 Kotlin 中,我们将会看到 NPE。

对于可为空的变量,我们必须用 "?" 将其特殊声明为一个具有空值的变量:

var b: String? = "abc"

而正常使用是这样的

var b: String = "abc"

如果我们直接调用 b 的话就会报错

val l = b.length()

那么,我们该如何使用可空变量呢?

我们可以使用安全操作符 "?." 对其进行方法调用,Like this:

b?.length()

如果 b 为空,则返回空;否则,调用 b.length()。

为什么要使用空安全操作符呢?

这使得我们解决了判空的问题,与 "!= null" 永远说再见,而且,空安全符在链式调用中会显得非常优美。

bob?.department?.head?.name

2.5 安全转换

在 Java 中,类型转换错误就会产生 ClassCastException,而在 Kotlin 中,我们可以通过安全转换 "as?" 来避免这个问题。

val aInt: Int? = a as? Int

如果转换失败,则返回空,否则返回正确数据。

2.6 类型检查

在 Java 中,校验一个类型可以使用 "instanceof",在 Kotlin 中,我们可以使用 "is"

fun getStringLength(obj: Any): Int? {  if (obj is String) {    return obj.length  }  return null}

2.7 区间

在 kotlin 中,区间表示一个范围,是 ClosedRange 的子类,IntRange 是最常用的。

写法:

  • 0..100 表示 [0,100] 包括100
  • 0 until 表示 [0,100) 不包括100
  • i in 0..100 判断 i 是否在区间 [0,100] 中 in

val range:IntRange = 0..100
val range_exculsive:IntRange = 0 until 100

2.8 集合

在 Kotlin 中,可以使用 in 操作符对集合进行迭代遍历。

for (item in items) {
    println(item)
}

Kotlin 更支持 lambda 对集合进行过滤等操作;

fruits .filter { it.startsWith("a") } .sortedBy { it } .map { it.toUpperCase() } .forEach { println(it) }

2.9 循环

for 循环

在 Kotlin 中,for 循环使用 in 关键字进行遍历。

val items = listOf("apple", "banana", "kiwi")
for (item in items) {
    println(item)
}
while 循环

这是 while 循环的一个简单示例

val items = listOf("apple", "banana", "kiwi")
var index = 0
while (index < items.size) {
    println("item at $index is ${items[index]}")
    index++
}
when 表达式

在 Kotlin 中使用 When 表达式来代替 Switch 语句,一个简单的示例如下:

fun describe(obj: Any): String = when (obj) {
    1 -> "One"
    "Hello" -> "Greeting"
    is Long -> "Long" 
    !is String -> "Not a string" 
    else -> "Unknown"
}

三、Kotlin 与 Java 不一样的地方

3.1 对象

Java 的写法

MainActivity.this

Kotlin 的写法

this@MainActivity

3.2 类

Java 的写法

MainActivity.class

Kotlin 的写法

MainActivity::class.java

3.3 继承

Java 的写法

public class MainActivity extends AppCompatActivity {

}

Kotlin 的写法(在 Kotlin 中被继承类必须被 open 关键字修饰)

class MainActivity : AppCompatActivity() {

}

3.4 变量

Java 的写法

Intent intent = new Intent();

Kotlin 的写法

var intent = Intent()

3.5 常量

Java 的写法

final String text = "";

Kotlin 的写法

val text = ""

3.6 静态常量

Java 的写法

public class MainActivity extends AppCompatActivity {

    static final String text = "";
}

Kotlin 的写法(需要注意的是要把静态变量定义在类上方)

const val text = ""

class MainActivity : AppCompatActivity() {

}

3.7 定义方法

Java 的写法

public void test(String message) {

}

Kotlin 的写法(Unit 跟 void 一样效果)

fun test(message : String) : Unit {

}

// 在 Kotlin 可以省略 Unit 这种返回值

fun test(message : String) {

}

3.8 重载方法

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)
    }
}

3.9 基本数据类型

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"

3.10 比较类型

Java 的写法

if ("" instanceof String) {

}

Kotlin 的写法

if ("" is String) {

}

3.11 转换符

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")

3.12 字符串比较

Java 的写法

String s1 = "text";
String s2 = "text";
if (s1.equals(s2)) {

}

Kotlin 的写法(Kotlin 对字符串比较的写法进行优化了,其他类型对象对比还是要用 equals 方法)

var s1 = "text"
var s2 = "text"
if (s1 == s2) {

}

3.13 数组

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")

3.14 循环

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])
}

3.15 角标循环

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])
}

3.16 高级循环

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)
}

3.17 判断器

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)
}

3.18 构造函数

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) {

}

3.19 类创建

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("Android轮子哥", 100);
person.setName("HJQ");
person.setAge(50);
System.out.println("name: " + person.getName() + ", age: " + person.getAge());

Kotlin 的写法(如果不想暴露成员变量的 set 方法,可以将 var 改成 val )

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("Android轮子哥", 100)
person.name = "HJQ"
person.age = 50
println("name: {$person.name}, age: {$person.age}")

3.20 私有化 set 方法

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
}

3.21 私有化 get 方法

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
}

3.22 枚举

Java 的写法

enum Sex {

    MAN(true), WOMAN(false);

    Sex(boolean isMan) {}
}

Kotlin 的写法

enum class Sex (var isMan: Boolean) {

    MAN(true), WOMAN(false)
}

3.23 接口

Java 的写法

public interface Callback {
    void onSuccess();
    void onFail();
}

Kotlin 的写法(Kotlin 接口方法里面可以自己实现)

interface Callback {
    fun onSuccess()
    fun onFail()
}

3.24 匿名内部类

Java 的写法

new Callback() {

    @Override
    public void onSuccess() {

    }

    @Override
    public void onFail() {

    }
};

Kotlin 的写法

object:Callback {

    override fun onSuccess() {

    }

    override fun onFail() {

    }
}

3.25 内部类

Java 的写法

public class MainActivity extends AppCompatActivity {

    public class MyTask {

    }
}

Kotlin 的写法

class MainActivity : AppCompatActivity() {

    inner class MyTask {

    }
}

3.26 内部类访问外部类同名变量

Java 的写法

String name = "Xmamiga";

public class MyTask {

    String name = "CCHIP";

    public void show() {
        System.out.println(name + "---" + MainActivity.this.name);
    }
}

Kotlin 的写法

var name = "Xmamiga"

inner class MyTask {

    var name = "CCHIP"

    fun show() {
        println(name + "---" + this@MainActivity.name)
    }
}

3.27 抽象类

Java 的写法

public abstract class BaseActivity extends AppCompatActivity implements Runnable {

    abstract void init();
}

Kotlin 的写法

abstract class BaseActivity : AppCompatActivity(), Runnable {

    abstract fun init()
}

3.28 静态变量和方法

Java 的写法

public class ToastUtils {

    public static Toast cToast;

    public static void show() {
        cToast.show();
    }
}

Kotlin 的写法(在 Kotlin 将这种方式称之为伴生对象)

companion object ToastUtils {

    var cToast : Toast? = null

    fun show() {
        cToast!!.show()
    }
}

3.29 可变参数

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
}

3.30 泛型

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")

3.31 构造代码块

Java 的写法

public class MainActivity extends AppCompatActivity {

    int number;

    {
        number = 1;
    }
}

Kotlin 的写法

class MainActivity : AppCompatActivity() {

    var number = 0

    init {
        number = 1
    }
}

3.32 静态代码块

Java 的写法

public class MainActivity extends AppCompatActivity {

    static int number;

    static {
        number = 1;
    }
}

Kotlin 的写法

class MainActivity : AppCompatActivity() {

    companion object {

        var number = 0

        init {
            number = 1
        }
    }
}

3.33 方法代码块

Java 的写法

void test(){
    {
        int a = 1;
    }
}

Kotlin 的写法

fun test() {
    run {
        var a =1
    }
}

3.34 可见修饰符

Java 的写法(默认为 default)

修饰符 作用
public 所有类可见
protected 子类可见
default 同一包下的类可见
private 仅对自己类可见

Kotlin 的写法(默认为 public)

修饰符 作用
public 所有类可见
internal 同 Module 下的类可见
protected 子类可见
private 仅对自己类可见

Refer