Kotlin基础初探
Posted Andoter的学习笔记
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin基础初探相关的知识,希望对你有一定的参考价值。
一、概述
Kotlin是一种在Java虚拟机上运行的静态类型变成语言,被称之为android世界的Swift,由JetBrains设计开发并开源。
Kotlin可以编译成Java字节码,也可以编译成javascript,方便在没有JVM的设备上运行。在Google I/O 2017中,Google宣布Kotlin成为Android官方开发语言。
Kotlin程序文件以.kt结尾,如:hello.kt。Kotlin的优点:
简洁:大大减少样板代码的数量
安全:避免空指针异常等整个类的错误
互操作性:充分利用JVM、Android和浏览器的现有库
工具友好:可用任何Java IDE或者使用命令行构建
二、开发环境搭建
IntelliJ IDEA环境搭建
有过使用Android Studio的经验的都知道,可以在设置里面的插件中搜索Kotlin插件,然后安装使用。具体步骤:Settings——>Plugins——>Install JetBrains plugins——>搜索Kotlin。
Kotlin Eclipse 环境搭建
Eclipse 通过 Marketplace 安装 Kotlin 插件,打开 Eclipse,选择 Help -> Eclipse Marketplace… 菜单,搜索 Kotlin 插件:
Kotlin Android 环境搭建
我们知道Android Studio也是基于IntelliJ IDELA开发组建而来,所以我们也是同样搜索Kotlin插件集成。
三、Kotlin基础语法
Kotlin文件以.kt结尾。
1、包声明
包名一般在Kotlin文件的开头进行声明。
package com.dsw.main
fun main(args: Array<String>) {
println("Hello World!")
}
Kotlin源文件不需要匹配相互匹配的目录和包名,源文件可以放在任何文件目录。如果没有指定包名,默认为default包。
在创建一个Java文件时,系统会默认给我们导入很多包。同样,在Kotlin文件中也会默认导入:
kotlin.*
kotlin.annotation.*
kotlin.collections.*
kotlin.comparisons.*
kotlin.io.*
kotlin.ranges.*
kotlin.sequences.*
kotlin.text.*
2、函数的定义
在Java中,我们通过:[修饰符] 返回值类型 方法名(参数)格式来定义一个函数。在Python中通过def来定义函数,那么在Kotlin中以关键字fun来定义函数,参数格式为:参数:类型,比如a:Int。
fun sum(a : Int, b : Int) : Int{
return a + b
}
上面的函数定义格式基本跟Java类似,既然Kotlin以简洁为长,那么我们看看还可以直接这么写。
fun sum(a : Int, b : Int) = a + b
直接将返回值表达式跟函数体卸载一起,同时省去返回值类型,返回值类型自动推断。public 方法则必须明确写出返回类型
public fun sum(a: Int, b: Int): Int = a + b
无返回值类型的函数
fun printSum(a: Int, b: Int): Unit {
print(a + b)
}
// 如果是返回 Unit类型,则可以省略(对于public方法也是这样):
public fun printSum(a: Int, b: Int) {
print(a + b)
}
可变长参数函数
函数的可变长参数用vararg关键字进行声明。
package com.andoter.dsw
fun main(args: Array<String>) {
print(sum(1,2,3,100))
}
fun sum(vararg v : Int) : Int{
var sum = 0
for (vi in v){
sum += vi
}
return sum
}
lambda函数
lambda表达式使用示例:
// 测试
fun main(args: Array<String>) {
val sumLambda: (Int, Int) -> Int = {x,y -> x+y}
println(sumLambda(1,2)) // 输出 3
}
3、变量和常量的定义
在Kotlin中通过var关键字定义可变的变量,val关键字定义不常量。
var <标识符> : <类型> = <初始化值>
val <标识符> : <类型> = <初始化值>
常量与变量都可以没有初始化值,但是在引用前必须初始化。编译器支持自动类型判断,即声明时可以不指定类型,由编译器判断。
val a: Int = 1
val b = 1 // 系统自动推断变量类型为Int
val c: Int // 如果不在声明时初始化则必须提供变量类型
c = 1 // 明确赋值
var x = 5 // 系统自动推断变量类型为Int
x += 1 // 变量可修改
4、字符串模板
在Java中我们通过format方法使用可以有字符串占位,同样在Kotlin中通过字符串模板实现:
$ 表示一个变量名或者变量值
$varName 表示变量值
${varName.fun()} 表示变量的方法返回值
fun main(args: Array<String>) {
var a = 1;
var b = "a is $a"
print(b)//输出a is 1
}
5、NULL检查机制
在Java语言开发中,我们经常会遇到NULLPointException的错误,而Kotlin语言就可以很好的解决这个问题。在Kotlin语言中,将系统中的类型分为可空类型和非空类型。String为不可空类型,String?为可空类型,如果将不可空类型赋值为null将会编译不通过。
package com.andoter.dsw
fun main(args: Array<String>) {
var b : String? = "abc"
print(b.length)
}
在上面的代码中,我们声明b是可空的,所以这样编译就不会通过,尽管它实际并没有为空。编译会提示:
Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
这句话的意思是我们可以使用?.或!!.来引用可为空对象。
fun main(args: Array<String>) {
var b : String? = "abc"
println(b?.length)
println(b!!.length)
}
6、类型检测及自动类型转换
Java中我们通过instance of来检测一个对象是否是某种类型,在Kotlin中通过is来进行检测。
fun getStringLength(obj: Any): Int? {
//obj在&&右边自动动转换成"String"类型
if (obj is String && obj.length > 0)
return obj.length
return null
}
7、区间
区间表达式由具有操作符形式 .. 的 rangeTo 函数辅以 in 和 !in 形成。step关键字用来指定步长,until关键字用来排除结束元素。
区间是为任何可比较类型定义的,但对于整型原生类型,它有一个优化的实现。以下是使用区间的一些示例:
for (i in 1..4) print(i) // 输出“1234”
for (i in 4..1) print(i) // 什么都不输出
if (i in 1..10) { // 等同于 1 <= i && i <= 10
println(i)
}
// 使用 step 指定步长
for (i in 1..4 step 2) print(i) // 输出“13”
for (i in 4 downTo 1 step 2) print(i) // 输出“42”
// 使用 until 函数排除结束元素
for (i in 1 until 10) { // i in [1, 10) 排除了 10
println(i)
}
四、Kotlin基本数据类型
Kotlin 的基本数值类型包括 Byte、Short、Int、Long、Float、Double 等。不同于Java的是,字符不属于数值类型,是一个独立的数据类型。
1、类型转换
这里同Java一样,高级数据类型可以隐式转换为低级数据类型,低级数据类型无法隐式 转换到高级数据类型。
package com.andoter.dsw
fun main(args: Array<String>) {
val b: Byte = 1 // OK, 字面值是静态检测的
val i: Int = b // 错误
}
每种数据类型都有下面的方法,可以转化为其它的数据类型:
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
2、位操作符
在Kotlin中提供了同Java一样的位操作符。
shl(bits):左移位(同Java<<)
shr(bits):右移位(同Java>>)
ushr(bits):无符号右移位(同Java>>>)
and(bits):与(同Java的&)
or(bits):或(同Java的|)
xor(bits):异或(同Java的^)
inv():取反(同Java的~)
package com.andoter.dsw
fun main(args: Array<String>) {
var index = 4
println("index.shl:" + index.shl(1))
println("index.shr:" + index.shr(1))
println("index.and(0):" + index.and(0))
println("index.or(0):" + index.or(1))
println("index.xor(0):" + index.xor(1))
println("index.inv():" + index.inv())
}
执行结果:
index.shl:8
index.shr:2
index.and(0):0
index.or(0):5
index.xor(0):5
index.inv():-5
3、字符类型
Char类型是用''包含起来的字符,比如'1','a'。字符字面值用单引号括起来: '1'。 特殊字符可以用反斜杠转义。 支持这几个转义序列:\t、 \b、\n、\r、\'、\"、\ 和 \$。 编码其他字符要用 Unicode 转义序列语法:'\uFF00'。
4、布尔类型
布尔用 Boolean 类型表示,它有两个值:true 和 false。内置的布尔运算有:
|| – 短路逻辑或
&& – 短路逻辑与
! - 逻辑非
5、数组
数组用类 Array 实现,并且还有一个 size 属性及 get 和 set 方法,由于使用 [] 重载了 get 和 set 方法,所以我们可以通过下标很方便的获取或者设置数组对应位置的值。数组的创建方式:
一种是通过arrayOf()
一种是通过Array工厂函数
package com.andoter.dsw
fun main(args: Array<String>) {
var array1 = intArrayOf(1,2,3)
var array2 = IntArray(3,{i -> i*3 })
printArray(array1)
printArray(array2)
}
fun printArray(array : IntArray){
for(arr in array){
System.out.print(arr)
}
}
6、字符串
和 Java 一样,String 是可不变的。方括号 [] 语法可以很方便的获取字符串中的某个字符,也可以通过 for 循环来遍历。
for (c in str) {
println(c)
}
Kotlin 支持三个引号 """ 扩起来的字符串,支持多行字符串,比如:
fun main(args: Array<String>) {
val text = """
多行字符串
多行字符串
"""
println(text) // 输出有一些前置空格
}
五、条件控制与循环
1、if表达式
一个 if 语句包含一个布尔表达式和一条或多条语句。
package com.andoter.dsw
fun main(args: Array<String>) {
var max = 5
if(max > 3){
print(">3")
}
}
IF表达式的结果也可以复制给一个变量。
package com.andoter.dsw
fun main(args: Array<String>) {
var a = 1
var b = 2
val c = if (a>=b) a else b
print(c)
}
使用区间
使用 in 运算符来检测某个数字是否在指定区间内,区间格式为 x..y 。
package com.andoter.dsw
fun main(args: Array<String>) {
var x = 4
if(x in 1..6){
print("在区间内")
}
}
2、When表达式
when 将它的参数和所有的分支条件顺序比较,直到某个分支满足条件。
when 既可以被当做表达式使用也可以被当做语句使用。如果它被当做表达式,符合条件的分支的值就是整个表达式的值,如果当做语句使用, 则忽略个别分支的值。when 类似其他语言的 switch 操作符。
fun main(args: Array<String>) {
var x = 5
when(x){
2-> print("2")
4-> print("4")
else-> print("else")
}
}
在 when 中,else 同 switch 的 default。如果其他分支都不满足条件将会求值 else 分支。
如果很多分支需要用相同的方式处理,则可以把多个分支条件放在一起,用逗号分隔:
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
在Kotlin中通过in或!in来判断一个值是否在某个区间,同样可以配合when进行使用。
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
3、For循环
for循环可以对任何提供迭代器的对象进行遍历。
for (item in collection) print(item)
如果要遍历一个数组或者集合,也可以这样:
for (i in array.indices) {
print(array[i])
}
比如下面对集合的遍历:
fun main(args: Array<String>) {
var items = listOf<String>("apple","banana")
for (item in items){
print(item)
}
}
4、while与do…while循环
while是最基本的循环,它的结构为:
while( 布尔表达式 ) {
//循环内容
}
do…while 循环 对于 while 语句而言,如果不满足条件,则不能进入循环。但有时候我们需要即使不满足条件,也至少执行一次。
do…while 循环和 while 循环相似,不同的是,do…while 循环至少会执行一次。
do {
//代码语句
}while(布尔表达式);
5、循环的返回与跳转
Java中有break、continue用于循环的跳转,同样Kotlin中也提供了三种结构化跳转表达式:
return:直接返回保卫他的函数
break:终止当前外围循环
continue:结束当前此次循环,进行下一次。
fun main(args: Array<String>) {
for (i in 1..10) {
if (i==3) continue // i 为 3 时跳过当前循环,继续下一次循环
println(i)
if (i>5) break // i 为 6 时 跳出循环
}
}
六、Kotlin类和对象
Java是面向对象编程,同样Kotlin也有类型,Kotlin类主要包含:构造函数和初始化代码块、函数、属性、对象声明。
Kotlin中通过关键字class声明类,后面紧跟类名:
class Andoter{
}
可见性修饰符
类、对象、接口、构造函数、方法、属性和它们的 setter 都可以有可见性修饰符。 (getter 总是与属性有着相同的可见性。) 在 Kotlin 中有这四个可见性修饰符:private、 protected、 internal 和 public。 如果没有显式指定修饰符的话,默认可见性是 public。
如果你不指定任何可见性修饰符,默认为 public,这意味着你的声明 将随处可见;
如果你声明为 private,它只会在声明它的文件内可见;
如果你声明为 internal,它会在相同模块内随处可见;
protected 不适用于顶层声明。
注意 对于Java用户:Kotlin 中外部类不能访问内部类的 private 成员。
类的属性
Kotlin中变量通过var或val定义,同样类的属性也属于变量一族,只需要将成员变量定义成一个变量,默认是 public 类型的。
class Andoter{
var name:String = "andoter"
var city:String = "HeFei"
}
在这里如果不对属性进行实例化,编译器会给我们提示:
Property must be initialized or be abstract
我们需要制定进行初始化,或者将该变量定义成abstract类型。
abstract class Andoters{
abstract var name :String
abstract var city : String
constructor()
}
getter和setter
属性的getter和setter声明语法:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
比如:
class Andoter{
var name:String
get() = name
set(value){
name = value
}
var city:String = "HeFei"
}
getter 和 setter 都是可选,并且getter、setter的访问限制级必须和变量是一致的,这点很让人费解,比如变量默认是public,你还定义setter和getter有啥用,直接对象就引用到了。
如果属性类型可以从初始化语句或者类的成员函数中推断出来,那就可以省去类型,val不允许设置setter函数,因为它是只读的。
主构造器
主构造函数是类头的一部分,类名的后面跟上构造函数的关键字constructor以及类型参数。主构造器中不能包含任何代码,初始化代码可以放在初始化代码段中,初始化代码段使用 init 关键字作为前缀。
fun main(args: Array<String>) {
var andoter = Andoter("andoter学习笔记")
print(andoter.name)
}
class Andoter constructor(name : String){
var name:String = "anodter"
var city:String = "HeFei"
init {
this.name = name
}
}
同时针对constructor关键字,也可以省略不写,那什么时候可以省略constructor关键字呢?
在构造函数不具有朱师傅或者默认的可见修饰符时,constructor关键字可以省略
默认的可见修饰符public,可以省略不写。
// 类似下面两种情况的,都必须存在constructor关键字,并且在修饰符或者注释符后面。
class Test private constructor(num: Int){
}
class Test @Inject constructor(num: Int){
}
辅助(二级)构造函数
Kotlin中支持二级构造函数。它们以constructor关键字作为前缀。
class Andoters{
var name :String = "Andoter"
var city : String = "HeFei"
constructor(name: String){
this.name = name
}
}
如果类有主构造函数,每个次构造函数都要,或直接或间接通过另一个次构造函数代理主构造函数。在同一个类中代理另一个构造函数使用 this 关键字。
package com.andoter.dsw
fun main(args: Array<String>) {
var andoter = Andoter("andoter学习笔记")
println(andoter.name)
var andoters = Andoter("Andoter学习笔记","AnhuiHefei")
print(andoters.name + andoters.city)
}
class Andoter constructor(name : String){
var name:String = "anodter"
var city:String = "HeFei"
init {
this.name = name
}
constructor(name :String, city : String): this(name){
this.name = name
this.city = city
}
}
七、抽象类
抽象是面向对象编程的特征之一,类本身,或类中的部分成员,都可以声明为abstract的。抽象成员在类中不存在具体的实现。
注意:无需对抽象类或抽象成员标注open注解。
abstract class Developer{
abstract var name:String
abstract var developDirection : String
}
class Andoter(override var name: String, override var developDirection: String) : Developer(){
var skill : String = "Android"
init {
this.name = name
this.developDirection = developDirection
}
}
八、嵌套类
class Outer { // 外部类
private val bar: Int = 1
class Nested { // 嵌套类
fun foo() = 2
}
}
fun main(args: Array<String>) {
val demo = Outer.Nested().foo() // 调用格式:外部类.嵌套类.嵌套类方法/属性
println(demo) // == 2
}
九、内部类
内部类使用 inner 关键字来表示。
内部类会带有一个对外部类的对象的引用,所以内部类可以访问外部类成员属性和成员函数。
class Outer{
private var outermem :String = "Andoter"
var v = "成员属性"
inner class Inner{
fun foo() = outermem
}
}
九、Kotlin继承
Kotlin 中所有类都继承该 Any 类,它是所有类的超类,对于没有超类型声明的类是默认超类。
class Example // 从 Any 隐式继承
Any 默认提供了三个函数:
equals()
hashCode()
toString()
注意:Any 不是 java.lang.Object。
如果一个类要被继承,可以使用 open 关键字进行修饰,不然该类是无法被别的Kotlin类继承的。
如果子类有主构造函数, 则基类必须在主构造函数中立即初始化。
open class Person(var name : String, var age : Int){// 基类
}
class Student(name : String, age : Int, var no : String, var score : Int) : Person(name, age) {
}
// 测试
fun main(args: Array<String>) {
val s = Student("Runoob", 18, "S12346", 89)
println("学生名: ${s.name}")
println("年龄: ${s.age}")
println("学生号: ${s.no}")
println("成绩: ${s.score}")
}
如果子类没有主构造函数,则必须在每一个二级构造函数中用 super 关键字初始化基类,或者在代理另一个构造函数。初始化基类时,可以调用基类的不同构造方法。
class Student : Person {
constructor(ctx: Context) : super(ctx) {
}
constructor(ctx: Context, attrs: AttributeSet) : super(ctx,attrs) {
}
}
在基类中,使用fun声明函数时,此函数默认为final修饰,不能被子类重写。如果允许子类重写该函数,那么就要手动添加 open 修饰它, 子类重写方法使用 override 关键词:
/**用户基类**/
open class Person{
open fun study(){ // 允许子类重写
println("我毕业了")
}
}
/**子类继承 Person 类**/
class Student : Person() {
override fun study(){ // 重写方法
println("我在读大学")
}
}
fun main(args: Array<String>) {
val s = Student()
s.study();
}
属性重写使用 override 关键字,属性必须具有兼容类型,每一个声明的属性都可以通过初始化程序或者getter方法被重写:
open class Foo {
open val x: Int get { …… }
}
class Bar1 : Foo() {
override val x: Int = ……
}
清明节放假因为车票买错了,也没回家。这两天花了一天的时间在菜鸟教程中把Kotlin的基础给看了下,总体来说是一副Java的语法糖,更像脚本语言的形式。
以上是关于Kotlin基础初探的主要内容,如果未能解决你的问题,请参考以下文章