Kotlin编程语言 之 入门基础
1.Kotlin介绍
- Kotlin 是一个基于 JVM 的新的编程语言,由 JetBrains 开发
- Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JVM的设备上运行
- JetBrains,作为目前广受欢迎的Java IDE IntelliJ 的提供商,在 Apache 许可下已经开源其Kotlin 编程语言
- Kotlin语言的使用场景:
- Kotlin 用于服务器端
- Kotlin 用于 JavaScript
-
Kotlin 用于 Android
- 划重点 - > Kotlin已正式成为Android官方支持开发语言,并且是Android开发一级语言
2.对Kotlin的评价
1.如何评价 Kotlin 语言? - 知乎
2.如何看待 Kotlin 成为 Google 正式支持的 Android 开发一级编程语言? - 知乎
3.Kotlin 作为 Android 开发语言相比传统 Java 有什么优势? - 知乎
4.个人评价
- 迁移:用Kotlin插件可将现有的Java代码,转换成Kotlin代码(单个、多个Java文件或对整个项目转换),虽然不能100%成功转换,但不成功的只需要微调下,就OK了
- 互操作性质:可以和Java进行混合开发 —— 这就厉害了,没有人生下来什么都会,所以每个人都会有从接触到熟悉的一个过程,复杂的Java可以在后期转换成Kotlin,给开发人员一个学习的过度,这使得Kotlin慢慢渗透到你项目中,最后替代掉项目中的每一个.java,Kotlin和Java语言的基础是差不多的,对于开发者,短时间上手问题不大
- 学习曲线:使用Kotlin后,项目的代码量确实减少不少,个人感觉Kotlin比Java8好很多(对Java8的一些新特性我其实还没闹明白...也不知道为毛,看一会就看不进去了,而这些天在学习Kotlin,我倒是求知欲爆发...)
- 工具:代码检查很严格,避免后期编译成功运行后出问题,再回过头苦逼的debug...毕竟有的项目较大,每一次编译都是需要时间的...
- 比较:没太深入了解过其他语言,就不和其他语言对比了
3.国际惯例 - Hello World
//新建一个Hello.kt,在文件中写入以下内容并运行,就可以看到Hello World!愉快的被打了出来...
fun main(args: Array<String>) {
println("Hello World!")
}
与Java相比,感觉代码少了和精简很多...(后面还有一段路要走,还有好多坑没有入...)
//Java Hello World
public class Hello{
public static void main(String[] args){
System.out.println("Hello World!")
}
}
4.项目搭建
- 因为Kotlin是JetBrains的自家语言,所以在IntelliJ IDEA中可以建立Kotlin的项目也是理所当然,IDEA Community版是免费的,可以做Kotlin开发
- 若你是Android开发者,新版Android Studio已经集成了Kotlin插件(没有的话更新或在Plugins里面安装Kotlin),通过工具栏 - > Code - > Convert Java File to Kotlin FIle,即可将Java文件转换成Kotlin(转换前切记备份项目,别玩脱了...)
- 如何搭建项目这里就不多说了,IDEA下载直达点此
- 以下是Kotlin中文网对几种构建工具的使用说明:
编写 Kotlin 代码文档
使用 Kapt
使用 Gradle
使用 Maven
使用 Ant
Kotlin 与 OSGi
编译器插件
5.基础语法
毕竟Kotlin是JVM的编程语言,所以后面会有一些Kotlin和Java相比较而言的话出现
-
分号";":Kotlin 中在每行代码的结尾省去了分号(;) ,如果你执意要写,IDE的语法检查最多也是建议删去,代码并不会因为多一个分号而不能被执行
我这些天学习过程中,总是打分号,写这些示例Demo时,手欠打了N个分号,后来都被我一个个的剔除了...(我的内心是拒绝分号的...可能是条件反射) -
package与import的基本用法和Java一样,基本用法相对Java来说,少了结尾的分号";"
-
属性
- 常量(val):类似Java中的终态即常量(final),一旦赋值后,则不能再次修改,官方称它为局部变量(Defining local variables),但我觉得定义在函数中并对其他成员不可见的情况才算是局部吧?应该称它为只读变量或常量吧,局部是什么鬼?
val a=1//自动推断出 `Int` 类型 val s="string"//自动推断出 `String` 类型 val temp:String//没有赋初始值,所以无法推断变量类型,所以类型不能省略! temp="fish_leong"
- 变量(var):和Java中的成员变量、局部变量类似用法一样,是可被重新定义赋值的
var a=1 a=2 var s="string" s="123"
- 伴生对象(companion object):类似Java中的静态(static)变量
//Test.kt class Test { companion object { var Global = "Hello Wordl!"//在伴生对象中添加Global属性 } } //Hello.kt fun main(args: Array<String>) { println(Test.Global)//在这里可以在未实例化Test情况下,获取或修改Test中的Global属性 Test.Global="fish_leong" println(Test.Global) }
-
Getters 与 Setters
声明一个属性的完整语法是var <propertyName>[: <PropertyType>] [= <property_initializer>] [<getter>] [<setter>]
- 初始器(initializer)、getter 和 setter 都是可选的。属性类型如果可以从初始器 (或者从其 getter 返回值)中推断出来,也可以省略
- 一个只读属性的语法和一个可变的属性的语法有两方面的不同:1、只读属性的用 val开始代替var 2、只读属性不允许 setter
//例如 var name: String get() { //get方法 return "1" } set(value) { //set方法 } //自 Kotlin 1.1 起,如果可以从 getter 推断出属性类型,则可以省略它 val test get() = 2//test被推断为Int类型
-
基本类型:与 Java 很相近
Type Bit width Double 64 Float 32 Long 64 Int 32 Short 16 Byte 8 -
类型的问题:由于Kotlin中的类型推断,会导致一些初学者不知道某个变量到底是什么类型,可以用下面这个方法曲线救国:
val intRange = 10..21//整型数区间 println(intRange.javaClass)//class kotlin.ranges.IntRange val doubleRange = 10.1..20.1//浮点数区间 println(doubleRange.javaClass)//class kotlin.ranges.ClosedDoubleRange val str = "fish_leong"//字符串 println(str.javaClass)//class java.lang.String
- 具体对类型的运算、转换等操作可参考官方基本类型文档:中文,英文
-
类型的问题:由于Kotlin中的类型推断,会导致一些初学者不知道某个变量到底是什么类型,可以用下面这个方法曲线救国:
-
权限修饰符:类似Java中的private、public...这些,可以修饰类或类中成员的修饰符,在下面 函数 例8中会讲到
-
函数:在Java里叫方法吧(Method),用fun定义
/** * 定义函数 * 权限修饰: * 参数:根据实际情况而定,可无,支持缺省参数 * 返回值:类型根据实际情况而定,可无,支持返回值类型推断 * 函数体:对于简单的逻辑可简写,下面会提到 */ 权限修饰 fun 函数名(参数名:类型 , ...):返回值类型 { //函数体,有多种写法 } //例1:返回值为Int的函数 fun add(x:Int,y:Int):Int{ return x+y } //例2:返回值为Int的函数,返回值由类型推断,相比Java要写return和花括号要简单些 fun add1(x: Int, y: Int) = x + y //例3:返回值为Int的函数,返回值由类型推断,若x>y,返回x...else... fun compare(x: Int, y: Int) = if(x>y) x else y //例4:不带返回值的函数, fun addPrint(x:Int,y:Int):Unit{ println(x+y) } //例5:不带返回值的函数,Unit可以省略 fun addPrint1(x:Int,y:Int){ println(x+y) } //例6:返回可为null的函数,由于Kotlin空安全机制,若返回类型String后不加?,则不能返回null fun toCustomString(x: Int, y: Int):String?{ if(x>y){ return x.toString() } return null } /** * 例7:默认参数的函数 * 默认参数,可以叫做缺省参数, * 缺省参数使用主要规则:调用时你只能从最后一个参数开始进行省略,换句话说,如果你要省略一个参数,你必须省略它后面所有的参数 */ fun defaultParam(content: String, a: Int = 2, b: Int = 2) { println(content + (a + b)) } fun main(args: Array<String>) { defaultParam("a+b=",1)//省略第三个参数 defaultParam("a+b=")//省略后两个参数 //想只省略第二个参数是不被允许的,因为第三个参数也是缺省参数,这样写不符合缺省参数的使用规则 } /** * 例8 权限修饰符 * 新建TestA.kt * TestA类,包含5种修饰函数 * private 、 protected 、 internal 、public 、open 和 不修饰 , 如果没有显式指定修饰符的话,默认可⻅性是 public * 所以public和不修饰算一种 */ class TestA { init { testPrivate()//private修饰的函数和Java一样,对本类成员可见 } /** * 只有用open修饰的函数,才能被子类覆写override * 被修饰的函数对本类成员、子类成员和外部都可见 */ open fun testOpen() { println(this) } /** * private修饰的函数和Java一样,对本类成员可见 */ private fun testPrivate() { println(this) } /** * protected修饰的函数,其属性和private⼀样,不一样的是这种函数对外部不可见,对子类成员可⻅ */ protected fun testProtected() { println(this) } /** * * 官方解释:能见到类声明的 本模块内 的任何客⼾端都可⻅其 internal 成员 * 若TestA类在A模块(Modules)中,TestB类在B模块(Modules)中,那么TestB类中将无法调用父类TestA中的internal修饰的函数 * 模块的概念, 一个模块是编译在一起的一套 Kotlin 文件: * 一个 IntelliJ IDEA 模块; * 一个 Maven 项目; * 一个 Gradle 源集; * 一次 <kotlinc> Ant 任务执行所编译的一套文件。 */ internal fun testInternal() { println(this) } /** * 没有加修饰符,和public修饰的函数是一样的 */ fun test() { println(this) } } /** * 新建TestB.kt * TestB类,继承于TestA类,不建议与TestA类在同一个模块(Modules)中,这样体现不出internal修饰的函数的作用 */ class TestB : TestA() { /** * 在override时,只能override父类中用open修饰的函数 */ override fun testOpen() { super.testOpen()//父类中用open修饰的函数,是可以被子类、外部调用或覆写override的 test()//没有加修饰符和加public修饰符是一样的,public可以省去 testInternal()//此方法在父类TestA中用internal修饰,同模块(Modules)中,对子类TestB成员可见,若TestA类和TestB不在同模块(Modules)中,那么该函数对子类不可见 /** * 测试internal修饰符的函数,可以建立两个模块进行测试 */ testProtected()//可以调用父类中protected修饰的函数,但是...请看下面 /** * 被protected修饰的函数,可以被父类和父类的成员变量或成员函数调用,但不能被外部调用,举个栗子: * var testA = TestA(); * testA.testProtected();//像这种情况,testProtected是不可见的 */ } }
点此了解:Kotlin之空安全
-
字符串模板
- 传统的字符串拼接,其实是很苦逼的,不但要写很多加好双引号进行连接,如果稍微写错位一个,将酿成大错!即使是String.format()也没有Kotlin的字符串模板强大
- Kotlin这种字符串模板方式,可以让我们从此不再苦逼
val a = "A" val b = "B" println("a的值是" + a + ",b的值是" + b)//传统写法 println("a的值是$a,b的值是$b")//Kotlin替代传统写法 println("a的值是${a.replace("A", "C")},b的值是$b")//Kotlin写法,在模板中写代码
-
if表达式的基本用法:基本用法与Java类似,但在Kotlin中还有些花式用法
//声明a b c,比较a和b,将大的赋值给c val a = 1 val b = 2 var c: Int //1.传统写法 if (a > b) { c = a } else { c = b } //2.Kotlin写法,表达式 c = if (a > b) a else b //3.Kotlin写法,if的分支可以是代码块,最后的表达式作为该块的值 c = if (a > b) { println(a) a } else { println(b) b }
-
Ranges(区间):通过in检测某变量是否在指定区间中
区间的所有使用方法请看Kotlin Range官方文档:中文,英文val a = 10 //10..100的类型是 IntRange if(a in 10..100){ println("a在10-100区间中") } //循环输出区间中的数字 for (s in 1..10) { println(s) }
-
when表达式的基本用法:类似Java中的switch...case,但Kotlin的when表达式似乎更强大
fun testWhen(x: Int) { when (x) { in 1..10 -> print("x在1-10的区间内")//区间判断 !in 20..21 -> print("x不在在20-21的区间内")//区间判断 is Int -> print("x是Int类型")//类型判断 1 -> println(1)//x=1 3 -> { println("退出") System.exit(0) } else ->//x均不符合其他条件 println("fish_leong") } } /** * when像if 一样,每一个分支可以是一个代码块,它的值是块中最后的表达式的值 */ fun testWhen2(x: Int) { val c = when (x) { 1 -> 2 2 -> 3 else -> 0 } }
-
for循环的基本用法
val arrays = arrayOf("1", "2", "3")//构造一个数组 for (element in arrays) { println(element)//循环打印出数组元素,类似Java中的for(String str : arrays){ } } for (pos in arrays.indices) { println(arrays[pos])//通过下标方式循环打印出数组元素,类似Java中的for(int i=*;i<*;i++) }
-
while与do while循环和Java一样,这里不多做介绍了
var a = 10 while (a > 0) { a-- } a = 10 do { a-- } while (a > 0)
-
类型检测及自动类型转换:Kotlin可通过is来推断一个表达式是否是指定的类型,类似Java中的instanceof,但功能比Java中的强大一些
//Any和Java中的Object差不多 fun main(args: Array<String>) { val str = "fish_leong" val int = 5 println(getObject(str)) println(getObject(int)) } fun getObject(obj: Any): Any { if (obj is String) { //如果这里写obj * 3是不可以的,因为在此块中,obj已经被推断是String类型,所以肯定不能做乘法计算 return obj.length//返回字符串长度 } if (obj is Int) { return obj * 3//这里被推断为Int类型,返回obj*2的结果 } return obj }
-
返回与跳转
-
return:默认从最直接包围它的函数或者匿名函数返回。它可以写表达式里:
fun main(args: Array<String>) { printUpCaseName(null) printUpCaseName("1") printUpCaseName("fish_leong") } /** * 检查name,转大写并输出 * @param name 名字 */ fun printUpCaseName(name: String?) { val nameCopy = name ?: return//如果name为null,则return,printUpCaseName方法结束,下面代码不会执行 if (name == "1") { //常见的return return } println(nameCopy.toUpperCase()) }
-
continue:继续下一次最直接包围它的循环。也可以使用类似Java循环标签,只是书写方式有点不一样:
fun main(args: Array<String>) { loop1@ for (i in 1..3) {//打印 1~3的数字 println("loop1:$i") loop2@ for (j in 5..8) {//打印 5~8的数字 if (j == 5) {//继续本循环 continue@loop2////在这里loop2写不写都可以,这个continue相对本循环的 } if (j == 6) {//跳出本循环,继续loop1标签的循环 continue@loop1 } println("loop2:$j") } //最后看输出,你会发现,loop2循环什么也没输出,因为每当j=6时,都会跳出loop2继续执行loop1,和下面的Java用法和接近 // loop1: // for (int i = 0; i < 3; i++) { // System.out.println("loop1:" + i); // loop2: // for (int j = 5; j < 8; j++) { // if (j == 5) { // continue loop2;//在这里loop2写不写都可以,这个continue相对本循环的 // } // if (j == 6) { // continue loop1; // } // System.out.println("loop2:" + j); // } // } // } } }
-
break:终止最直接包围它的循环。标签用法和continue一样:
fun main(args: Array<String>) { loop1@ for (i in 1..3) {//打印 1~3的数字 println("loop1:$i") loop2@ for (j in 5..8) {//打印 5~8的数字 if (j == 5) {// break@loop1//终止loop1循环,loop2循环自然也会终止 } println("loop2:$j") } } }
-
标签处返回:Kotlin 有函数字面量、局部函数和对象表达式。因此 Kotlin 的函数可以被嵌套。 标签限制的 return 允许我们从外层函数返回。 最重要的一个用途就是从 lambda 表达式中返回:
输出fun main(args: Array<String>) { test(arrayOf(1, 2, 3, 4)) } fun test(value: Array<Int>) { value.forEach for1@ { // it代表forEach循环访问value数组中元素时的某个元素 if (it == 1) return@for1//当循环访问到等于1的元素时,则返回至标签处,下一行代码不执行,forEach不会因此停止,还会继续循环访问后面的元素 println("for1$it") } value.forEach for2@ { if (it == 1) return//这个厉害了,此return是相对test函数的,一旦条件满足,后面代码将不会被执行 println("for2$it") } println("函数结束") }
for1:2 for1:3 for1:4
-
return:默认从最直接包围它的函数或者匿名函数返回。它可以写表达式里:
-
编码规范
就没什么可以白话的了...搬运自Kotlin 语言中文站-
命名风格
如果拿不准的时候,默认使用Java的编码规范,比如:
使用驼峰法命名(并避免命名含有下划线)
类型名以大写字母开头
方法和属性以小写字母开头
使用 4 个空格缩进
公有函数应撰写函数文档,这样这些文档才会出现在 Kotlin Doc 中 -
冒号
类型和超类型之间的冒号前要有一个空格,而实例和类型之间的冒号前不要有空格:
interface Foo<out T : Any> : Bar { fun foo(a: Int): T} -
Lambda表达式
在lambda表达式中, 大括号左右要加空格,分隔参数与代码体的箭头左右也要加空格 ,lambda表达应尽可能不要写在圆括号中list.filter { it > 10 }.map { element -> element * 2 }
在非嵌套的短lambda表达式中,最好使用约定俗成的默认参数 it
来替代显式声明参数名 ,在嵌套的有参数的lambda表达式中,参数应该总是显式声明 -
类头格式化
有少数几个参数的类可以写成一行:class Person(id: Int, name: String)
具有较长类头的类应该格式化,以使每个主构造函数参数位于带有缩进的单独一行中。 此外,右括号应该另起一行。如果我们使用继承,那么超类构造函数调用或者实现接口列表应位于与括号相同的行上:
class Person( id: Int, name: String, surname: String) : Human(id, name) { // ……}
对于多个接口,应首先放置超类构造函数调用,然后每个接口应位于不同的行中:
class Person( id: Int, name: String, surname: String) : Human(id, name), KotlinMaker { // ……}
构造函数参数可以使用常规缩进或连续缩进(双倍的常规缩进)
-
Unit
如果函数返回 Unit 类型,该返回类型应该省略:fun foo() { // 省略了 ": Unit"}
-
函数还是属性
很多场合无参的函数可与只读属性互换。 尽管语义相近,也有一些取舍的风格约定
底层算法优先使用属性而不是函数:
- 不会抛出任何异常
- O(1)
- 复杂度
- 计算廉价(或缓存第一次运行)
- 不同调用返回相同结果
-
还有好多东西没有提到,例如集合、类与对象等,会在以后文章中写,上面这些算是入门的基础吧。
参考文档:
[1]基本语法 - Kotlin 语言中文站
[2]控制流:if、when、for、while - Kotlin 语言中文站
[3]编码规范 - Kotlin 语言中文站
[4]返回与跳转 - Kotlin 语言中文站
[5]模块 - Kotlin 语言中文站
[6]Kotlin - 百度百科
[7]缺省参数 - 百度百科
网友评论