Kotlin:基础语法学习(二)

本篇内容清单如下:

  • 函数
  • 变量
  • 基本类型
  • 控制流

一、基本语法

1. 包定义与导入

  • 同 Java,在文件顶部声明:package(修饰符) 包名
  • 源文件的 所有内容(类、函数)都包含在 声明的 包内。
  • 若没有指明包名,则文件内容属于 无名字的 默认包。

导入:

  • 导入包:import packageName
  • 若出现类名冲突,则可使用 as 关键字本地重命名消除歧义。【1】
import org.example.Message
import org.test.Message as TestMessage

import 关键字使用场景:

  • 导入「类」
  • 导入 「顶层函数 及 属性」
  • 导入 在 「对象声明」 中声明的函数和属性;
  • 导入「枚举常量」

2. 程序入口

kotlin 应用程序的入口点是 main 函数。(虽如此,但是以下 main 无法再 AS 中识别。)

fun main() { println("hello word !") }

// 可识别运行的 main 方法
companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            println("hello word !")
        }
}

3. 函数

  • 声明:使用 fun 关键字。
  • 形式:fun 函数名(参数名: 类型, 参数名: 类型): 返回值类型 { // 函数体 } 。每个参数,必须有显式类型。
  • 调用:sum(1 + 2)

一个加法函数的两种写法:

// 1. 完整写法
fun sum(a: Int, b: Int): Int {
  return a + b
}

// 2. 函数体仅含 单个表达式,并且 返回值 可 自动推断类型
fun sum(a: Int, b: Int) = a + b

// 3. 返回值无意义(类比 void),则 Unit 返回类型可省略
fun printSum(a: Int, b: Int): Unit {  // =>  fun printSum(a: Int, b: Int) { ... }
   println("sum of $a and $b is ${a + b}")
}

以上为一般函数调用,当然还有其他函数类型写法与调用,存在一些不同。同函数相关的内容主要有以下这五点:

  • 函数声明
  • 函数参数
  • 函数返回值
  • 函数调用
  • 函数作用域

与 Java 很大不同的内容点:

  • 默认参数 && 具名参数 && 可变参数
  • 中缀表示法
  • 局部函数(闭包)
  • 扩展函数
  • 高阶函数 && Lambda 表达式

根据文档整理出来的导图(函数作用域后续文章还会继续说道)如下:


Kotlin 函数相关概念总结

[说明:星星 - 新概念且重要,问号 - 暂不理解,红旗 - 新概念但不重要。]

备注:

  • 主构造函数很特别,参数可以使用 val 或 var 声明,但是普通函数不能用。次构造函数没啥大差别。
class Person constructor() { }

class Person constructor(name: String)

class Person(name: String)

class Person public constructor(name: String)

class Person(name: String) {
    var children: MutableList<Person> = mutableListOf()
    // 次构造函数,必须要委托给 主构造函数
    constructor(name: String, parent: Person)  : this(name) {
        parent.children.add(this)
    }
}

4. 变量

两种类型:

  • 只读变量,使用关键字 val 定义。只能为其赋值一次。
  • 可重新赋值的变量,使用关键字 var 定义。

变量又可分为:

  • 顶层变量:在文件最顶部声明的变量
  • 属性与字段:类中或函数中声明与定义。
val a: Int = 1 // 立即赋值
val b = 2       // 自动推动出类型,可省略 参数类型
// b = 3         // 重新赋值,编译器会报错
 
var x = 5      // 自动推断类型
x += 5

二、基本类型

在 Kotlin 中,也是 “一切皆为对象”。但有些类型 可以在运行时,表示为「原生类型值」,如 数字、字符以及布尔值。

Kotlin 中的基本类型:

  • 数字:Byte、Short、Int、Long、Float、Double
  • 字符:Char
  • 布尔值:Boolean / true false
  • 数组:Array
  • 字符串:String
1. 数字
  • Long、Float、Double 类型同 Java 一致,后面可带 L、f/F、D 。
  • Kotlin 中「无隐式拓宽转换」,如具有 Double 类型参数的函数,只能接收 Double 类型的值,而不能对 Float、Int 或其他数字值调用。
  • 若需要原生类型 对应的 可空引用泛型,如 Int 可空引用对应为 Int?,后者会把数字进行装箱。 [注:数字装箱不一定保留同一性。=== 判断同一性。 ]

每个数字类型 都支持如下 转换函数:

  • toByte(): Byte
  • toShort(): Short
  • toInt(): Int
  • toLong(): Long
  • toFloat(): Float
  • toDouble(): Double
  • toChar(): Char
2. 字符
  • 不能直接当做数字。
  • 字面值使用单引号 '1' 。
3. 布尔
  • 同 java ,值 true 与 false 。
  • 布尔运算:|| 、 &&、 !
4. 数组
  • 用 Array 表示,定义可 get 与 set 函数,可以转变为 []
  • 构建数组:arrayOf(1, 2, 3)
  • 原生类型数组:ByteArray、IntArray 等。
5. 字符串
  • 用 String 表示。字符串是不可变的,元素访问,可用索引运算符访问:s[i]
  • 访问字符串:for (c in str) { } 、str[0]
  • 拼接字符串,可以使用操作符 +,但不建议,一般都使用 字符串模板 $原始字符串
原始字符串
  • 原始字符串:使用三个引号 (""") 分解符,内部 无转义 且 包含换行以及任何其他字符。""" println("hello world!")""" 。另可默认 | 作为边界前缀。
var text = """
    for (c in "foo") 
        print(c)
"""

// 去除前导空格
var text = """
  |Tell me and I forget.
  | Teach me and I remember.
""".trimMargin() 
字符串模板
  • 字符串字面值 可使用 模板表达式,以 美元符 $ 开头。
  • 单个变量,可省略花括号,如 i ;表达式,则使用{a + b} 。

整理导入如下:


Kotlin 基本类型汇总

三、控制流

同 Java 有些差别,Kotlin 控制类分为:

  • if
  • when
  • for
  • while

以下仅整理不一致的点。

if 表达式
  • 若为单个 if 表达式,则可以直接 返回值或 赋值给变量,但必须要有 else 分支。
val max = if (a > b) a else b
when 表达式
  • 取代了 C 语言的 switch 语句。
  • 分支条件,可以是单个变量,也可以是 某个表达式,只要满足条件,则执行该分支的内容。
  • 若 多个分支 处理方式一致,则可以将分支条件放在一起,用 , 隔开。
val x = 3
when (x) {
      1           -> print("x == 1")
      2,3         -> print("x > 1")
      "1".toInt() -> print("it's 1")
      in 1..10    -> print("x is in the range")
      !in 10..20  -> print("x is outside the range")
      is Int      -> print("x is Int type")
      else        -> print("none of above")
}
Break 与 Continue 标签
  • Kotlin 中任何表达式都可以使用 标签 来标记。标签的格式:@标识符
  • 函数嵌套,可返回到指定标签处。

四、习惯用法

五、编码规范

六、DEMO 练习

首先写一个 Java Demo,然后对比一下 Kotlin 的写法。具体内容为:一个 User Model 类,一个 UserManager 单例类,再加上 main() 入口函数调用(均是内部静态类写法)。

这里面涉及到关于 kotlin 的内容点:

  • 变量声明
  • 函数声明
  • 空值与 null 检测
  • 类与对象
  • 单例类
  • 静态方法

这是我之前学 kotlin 时,特别想知道如果 kt 来写,会是怎样的。当然对于 static 修饰的变量和方法,在 kt 中都是包裹在 compion object 中,称为「伴生对象」,这一点首次接触,感觉非常新奇。

以下为 Java 实现:

public class JavaDemo {

    public static void main(String[] args) {
        User user = new User();
        user.name = "cc";
        user.age = 24;
        user.address = "Shanghai";

        UserManager.getInstance().addUser(user);
        UserManager.getInstance().addUser(new User("kk", 25));
    }

    public static class UserManager {

        private static UserManager sInstance;

        private List<User> users = new ArrayList<>();

        public static UserManager getInstance() {
            if (sInstance == null) {
                synchronized(UserManager.class) {
                    if (sInstance == null) {
                        sInstance = new UserManager();
                    }
                }
            }
            return sInstance;
        }

        private UserManager() {}

        public void addUser(User user) {
            if (!users.contains(user)) {
                users.add(user);
            }
        }

        public void addUsers(List<User> users) {
            if (!this.users.containsAll(users)) {
                this.users.addAll(users);
            }
        }

        public void removeUser(User user) {
            users.remove(user);
        }

        public void removeByIndex(int index) {
            users.remove(index);
        }

        public void clear() {
            users.clear();
        }
    }

    public static class User {
        private String name;

        private int age;

        private String address;

        public User() {}

        public User(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;
        }

        public String getAddress() {
            return address;
        }

        public void setAddress(String address) {
            this.address = address;
        }
    }
}

Kotlin 写法如下:

import java.util.*

class JavaVSKotlinDemo {

    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            val user = User()
            user.name = "cc"
            user.age = 24
            user.address = "Shanghai"

            UserManager.getInstance().addUser(user)
            UserManager.getInstance().addUser(User("kk", 25))
        }
    }

    class UserManager private constructor() {

        private val users = ArrayList<User>()

        fun addUser(user: User) {
            if (!users.contains(user)) {
                users.add(user)
            }
        }

        fun addUsers(users: List<User>) {
            if (!this.users.containsAll(users)) {
                this.users.addAll(users)
            }
        }

        fun removeUser(user: User) {
            users.remove(user)
        }

        fun removeByIndex(index: Int) {
            users.removeAt(index)
        }

        fun clear() {
            users.clear()
        }

        companion object {
            // 单例模式
            private var sInstance: UserManager? = null
                get() {
                    return field ?: UserManager()
                }

            @JvmStatic
            @Synchronized
            fun getInstance(): UserManager {
                // synchronized(UserManager::class.java)
                return requireNotNull(sInstance)
            }
        }
    }
                                                                     
    class User constructor() {
        var name: String? = null

        var age: Int = 0

        var address: String? = null

        constructor(name: String?, age: Int) : this() {
            this.name = name
            this.age = age
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容