Kotlin是JetBrains推出项目,是一种在Java虚拟机上运行的静态类型编程语言,也可以被编译成为JavaScript源码。在2011年7月推出之前,它已被开发一年之久。直到2016年2月15日,官方发布了第一个稳定的release版本 Kotlin v1.0。
基于JVM编程,这意味着Kotlin可以写服务器代码;
基于Android和Browser编程,这意味着不仅可以写客户端编程,连前端开发都可以;
基于Native编程,直接绕过JVM与底层代码打交道。
2010立项
2011.6对外公开
2012.2开源
2013.8支持Android Studio
2014.6 全新的开源web站点和域名 Kotlinlang.org
2016.2发布1.0
2016.9发布1.0.4,支持apt
Kotlin中的Bolean类型和Java中的一样,只有两个值true和false。
与Java一样可以使用&&、||、!进行与或非操作
eg:
//Boolean
var num: Boolean = true
主要包括整型和浮点型 跟java一样
分类 | 类型 | 位宽 |
---|---|---|
浮点型 | Double | 64 |
浮点型 | Float | 32 |
整型 | Long | 64 |
整型 | Int | 32 |
整型 | Short | 16 |
字节 | Byte | 8 |
eg:
//1.双精度 double
val aDouble: Double = 1.0
//2.Float
val aFloat: Float = 0.111F
//4.Int
val aInt: Int = 123123
//5.Short
val aShort: Short = 223
//6.long
val aLong: Long = 312381923882
//7.char
val aChar: Char = 'd'
//8.string
val aString: String = "啦啦阿拉阿娇活动空间撒成本,啊,是否萨克的纪念萨科的萨才能"
eg:
//十进制
var a1 = 1
//十六进制
var a2 =0x1
//var a3 =01
//二进制
var a4 = 0b00000
//指数形式
var a5 = 1.2E10
占两个字节、表示一个16位的Unicode字符,单引号 ‘’ 引起来
eg:’a’ ‘0’ ‘\n’
转义字符 | 含义 |
---|---|
\t | 制表符 |
\b | 光标后退一个字符 |
\n | 光标回到行首 |
\r | Int |
\’ | 单引号 |
\” | 双引号 |
\ | 反斜杠 |
$ | 美元符号,Kontlin支持美元符号开头的字符串模版 |
eg:
//hello "张三"
println("hello\"张三\"")
//$1000
println("\$1000")
不支持隐式转换
eg:
var anInt: Int = 2
var numLong: Long = anInt.toLong()
“一串”Char,用双引号 “” 引起来
eg:
val string1: String = "HelloKotlin"
var stringChars: String = String(charArrayOf('H', 'e', 'l', 'l', 'o', 'K', 'o', 't', 'l', 'i', 'n'))
string1 == stringChars 等价于equals
string1 === stringChars 比较对象是否为同一个 比较对象的引用值
格式的输出,支持字符串、表达式、函数
eg:
val string1: String = "HelloKotlin"
fun a():String{
return ",你好!"
}
println("${string1}!")
println("${1+3}")
println("张三${a()}")
不在区分拆箱和装箱
类是一个抽象的概念,是具有某些特征的事物的概括,不特定指代任何一个具体的事物
eg: 人、动物 …
定义:
和Java一样,Kotlin中使用关键字class来声明一个类。
//没有属性、方法的a类
class a{}
类的申明包含三部分:类名、类头(指构造参数、构造方法等)、类体(用大括号包裹的部分)。类头和类体这两个部分并非必要的。
//没有属类头,类体的a1类
class a1
//申明属性,并初始化
class a2{
var name = "张三"
var age = 12
}
Kotlin提供了延迟初始化的方式来解决初始化的问题,使用关键字lateinit即可。
注:Kotlin并不支持对原生类型进行lateinit,因为Kotlin会使用null来对每一个用lateinit修饰的属性做初始化,而基础类型是没有null类型,所以无法使用lateinit。
//申明属性,并初始化,address延迟初始化
class a3{
var name = "张三"
var age = 12
lateinit var address: String
}
var a = a3()
a.address="丁家桥"
println("姓名:\t${a.name}")
println("年龄:\t${a.age}")
println("地址:\t${a.address}")
Kotlin的类会对每个声明的属性自动生成对应的get/set方法,只读类型的val只有get方法,可变类型的var有get/set方法。相比Java的get/set方法,Kotlin显得要简洁得多。
class a3{
var name = "张三"
var age = 12
get() {
//name的get/set方法输出一句话 field 是name 正真的值
println("我是${name}")
return field
}
Kotlin提供的备用字段(关键字field)的使用范围仅限于属性的get/set方法。
使用场景:属性需要在对象创建时就进行初始化,则需要用到自定义构造函数了。
Kotlin中的类可以拥有一个主构造函数以及多个次构造函数,主构造是类头的一部分,使用关键字constructor声明并且跟在类名之后。
//a4 属性name,age
class a4(name: String, age: Int) {
var name = name
var age = age
}
构造函数中的name和age是形参,类体中的name和age是类的成员变量。
当构造函数没有注解或者可见性声明时,constructor关键字可以省略不写。
但如果构造函数带有可见性声明和注解时,constructor关键字则不可省略,且可见性声明应该在constructor关键字之前。
//带有可见性申明
class a5 public constructor(name: String, age: Int) {
var name = name
var age = age
}
Kotlin类的主构造函数不能包含任何代码,所以,如果你想做一些初始化操作,可以使用init初始化代码块来完成。
class a5 public constructor(name: String, age: Int) {
var name = name
var age = age
init {
if (name != "李四") {
this.name = "李四"
}
}
}
Kotlin中,类属性既可以在声明出直接进行初始化,也可以在init代码块中进行初始化操作。(个人认为直接在实体中把形参赋给成员变量会直观一点)
次级构造函数不能省略consructor关键字。
当类拥有主构造函时,任何一个二级构造函数都需要直接或间接通过另一个二级构造函数代理主构造函数。
类中的一个构造函数代理另一个构造函数,需要使用关键字this。
class a6(age: Int) { //z1 主构造函数
var name = ""
var age = age //z1 初始化age
var address = ""
constructor(name:String,age: Int):this(age){ //c1 次构造函数1,直接代理主构造函数 z1
this.name = name
}
constructor(name: String,age: Int,address:String):this(name,age){ //c2 次构造函数2,代理次构造函数c1,间接代理主构造函数z1
this.address = address
}
}
任意类型都有可空和不可空两种
Kotlin很大的个特点就是null安全,Kotlin用 ? 来区分是否为空。
eg:
1.var b1: String? = "xiaoxianxian" //b1 可空
println(b1?.length) //? 如果b1不为空返回length否则返回null
2.var b1: String? = "xiaoxianxian" //b1 可空
println(b1!!.length) //告诉编译器放心大胆执行哈哈
open class B1 {
fun getB1() {
println("我是B1")
}
}
class B2 : B1()
var a: B1 = B2()
if (a is B2) {
a.getB1()
}
如果我在前面通过某些条件判断出来了,实际上已经是某个类型了,后面就无需在判断了,直接使用该类型的特性
编译器会尽可能的推倒出类型,远离无用的类型转换
命名空间,包的申明必须在非注释代码的第一行,类的全名
表范围
//[0-1024]
val range:IntRange = 0..1024
//[0-1023] 半开区间[1-1024)
val range1:IntRange = 0 until 1024
println(1025 in range)//false 1025 不在0..1024范围内
跟Java中一样,都是一系列的对象 1,2,3…
关键字Array,要创建数组就得使用arrayOf
eg:
var arrayOfint: IntArray = intArrayOf(1, 2, 6, 4, 45, 2, 5, 78, 90)
var arrayOfchai: CharArray = charArrayOf('你', '好')
var arrayOfString: Array<String> = arrayOf("美少女!啦啦啦")
//数组长度、遍历数组结果集、替换2为0
println(arrayOfint.size)
arrayOfint[1] = 0
for (i in arrayOfint) {
print(i)
}
//char 连接字符 joinToString
println(arrayOfchai.size)
println(arrayOfchai.joinToString(""))
//字符串切片, 1.intArray 第2到第5个元素, 2.使用区间
println(arrayOfint.slice(1..4))
arrayOfNulls可以创建一个指定长度,内容为空的数组
关键字Array,要创建数组就得使用arrayOf
var arrays = arrayOfNulls(5)
var 普通的可变的变量,可读和可写。
val 只读的变量。相当于Java中用final修饰的变量,必须初始化赋值。
var abc = "哈哈"
val abcd = "哦"
fun test() {
abc = "haha"
//abcd="ooo"
println("var abc\t${abc}")
}
访问控制符 fun 方法名(参数,参数,参数) : 返回值类型{}
访问控制符:Kotlin的访问范围从大到小分别是public、internal、protected、private。
不声明默认是public。
返回值类型:不需要返回类型的函数,后面的:返回值类型可以缺省。
(名字在前类型在后,函数也是一个类型,可以被赋值,传递)。
//有返回值
fun getTitle():String{
return "zhangsan"
}
//无返回值
fun geta(a:String){
println("${a}")
}
//(具名函数)函数sum 计算两数和
fun sum1(num1: Int, num2: Int): Int {
println("$num1+$num2=${num1 + num2}")
return num1 + num2
}
Kotlin支持包级函数,即函数不依赖于类而存在。
没有名字的函数,无名氏。需赋值给xx变量。
fun (参数,参数,参数) : 返回值类型{}
eg:
var num1 = fun(num: Long) {
print("${num}")
}
//匿名函数 同上
var sum2 = fun(num1: Int, num2: Long): Long {
println("$num1+$num2=${num1 + num2}")
return num1 + num2
}
注:
功能单一
函数名顾名思义
参数个数不要太多,难理解影响效率。
var res = geta("1111111111111")
println(res)
其实就是匿名函数
Lambda 表达式的完整语法形式
var sum: (Int, Int) -> Int = { x, y -> x + y }
eg:
var sum4 = { num1: Int, num2: Long -> num1 + num2 } //num1,num2参数,num1+num2返回值
lambda 表达式总是被大括号括着, 完整语法形式的参数声明放在括号内,并有可选的类型标注。
函数体在 -> 右边。
返回值:函数体的最后一行。
//直接调用sum4
println(sum4(1, 9))
sum4(1, 9) //等价于 sum4.invoke(1,9)
//便利数组for
for (i in args) {
print(i)
}
//遍历数组 forEarch
args.forEach {
println(it)
}
如果传的函数和需要接收的Lambda表达是的类型是完全一样的,还可继续简化。
具名函数加 :: 相当于引用。
类型:Function0 – Function22 最多可以有23个参数。
() -> Unit 无参返回Unit
(Int) -> Int 传入整型的参数并返回一个Int
函数参数调用时最后一个Lambda可以移除去。
函数参数只有一个Lambda,调用时小括号可省略。
Lambda只有一个参数名字可默认为it
入参、返回值与形参一致的函数可以用函数引用的方式做为实参传入
//同上
args.forEach ({ element -> println(element) })
//如果最后一个参数为lambda表达式 参数可以移到()外面
args.forEach (){ println(it) }
//()无用
args.forEach (){ println(it) }
//println接收一个参 Any println做为参数传给forEach
//forEach的参数和println 的参数相同
args.forEach(::println)
属性:或成员变量,类范围的变量。
方法:或成员函数,类范围的函数
与普通函数完全一致,类中。
//user类中获取name的方法
fun getName(name:String){
println("你好,${name}")
}
//调用
user.getName(“张三”)
构造方法参数中val/var 修饰的都是属性。
类的累部也可以定义属性。
class user1(var name: String , age: Int) { //age 只是一个构造方法的参数非属性
var number: Int = 0
var age = 0
}
class user(var name: String) {
var number: Int = 0
var age = 0
//age 的get/set方法输出一句话 field = age 正真的值
get() {
println("我是number")
return field
}
set(value) {
field = value
}
属性的初始化尽量在构造函数中完成。
var用lateinit进行延迟加载,val 用lazy进行延迟加载。
类的内部也可以定义属性。
eg:
class B
class A{
var a=0
lateinit var a1:String //延迟初始化
val a2:B by lazy{
println("init B")
B()
}
与Java中的if类似
fun ifa(a: Int, b: Int) {
if (a > b) {
println("${a}")
} else {
println("${b}")
}
kotlin使用when加强版的switch语句,基本特性都跟switch差不多会对列出来的分支进行匹配满足条件的分支会被执行。
when可做表达式或语句。
val age = 17
val type = when(age){
0 -> "baby"
in 1..12 -> "孩子"
in 13..19 -> "少年"
else -> "成年人"
}
println(type)
var num = 99
when (num) {
is Int -> println("Hello $num")
in 1..100 -> println("$num is 1..100")
!in 1..100 -> println("$num is 1..10")
else -> println("num")
}
与java for相同。
//遍历args
for (arg in args){
println(arg)
}
//index value
for ((index,value) in args.withIndex()){
println("$index - $value")
}
与Java 中完全相同
跳过当前循环continue
终止循环break
var a = 5
while (a>0){
println(a)
a--
}
do {
println(a)
a--
}while (a>0)
catch 分支匹配异常类型 可为表达式或赋值
finally 不管代码是否抛异常,都会执行
eg:
try {
var num1 = args[0].toInt()
var num2 = args[1].toInt()
println("$num1+$num2=${num1 + num2}")
}catch (e:NumberFormatException){
//1 as
println("请输入正确的数字")
}catch (e:ArrayIndexOutOfBoundsException){
//args.size = 0
println("请输入数字")
}catch (e:Exception){
println("未知异常${e.message}")
}finally {
println("谢谢使用")
}
//xx/0=0
var res =try {
args[0].toInt()/args[1].toInt()
}catch (e:Exception){
0
给函数的实参赋上形参
eg:
fun sum(num1:Int,num2:Int)=num1+num2
//有名
println(sum(num1=3,num2 =9))
某个参数可以接收多个值,可不为最后一个参数,如传参有奇异时可以使用具名参数。
vararg 关键字 java中变长参数只能为最后一个参数
eg:
//vararg
fun main(vararg args:String) {
args.forEach(::println)
}
fun hi(num: Double, vararg ars: Int, num2: String) {
ars.forEach(::println)
println(num)
println(num2)
}
//变长
hi(1.0,1,1,1,num2 = "hahh")
var a = intArrayOf(1,2,5,34,4)
hi(1.0,*a,num2 = "hahh")
fun hi1(num: Double=5.9, vararg ars: Int, num2: String) { //使用时不传使用默认值 反之
ars.forEach(::println)
println(num)
println(num2)
}
//默认 参数1始终给num 具名
hi1(ars=*a,num2 = "hahh")
默认值可为任意位置
含有抽象方法的类称为抽象类。
在抽象类中,不仅可以有抽象方法,同时可以有具体实现的方法。
abstract class a{
//抽象方法a
abstract fun a()
}
抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。
抽象类不能用来创建对象;
如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。如果抽象类中含有抽象属性,再实现子类中必须将抽象属性初始化,除非子类也为抽象类。
只有声明,而没有具体的实现。
abstract fun a()
抽象方法必须用abstract关键字进行修饰
抽象方法不用手动添加open,默认被open修饰
抽象方法没有具体的实现
含有抽象方法的类成为抽象类,必须由abtract关键字修饰
抽象属性就是在var/val前被abstract修饰。
abstract var a:String
abstract var a1:Int
抽象属相在抽象类中不能被初始化。
抽象属性只能在抽象类中声明。
和java一样,kotlin中的接口也是使用interface关键字。
interface users {
fun a()
}
和java不同的是,接口的实现不是使用implement关键字,而是冒号:,和继承一样。所以我认为kotlin中的接口是一种特殊的抽象类。
class usersImpl:users {
override fun a() {}
}
和java一样,接口可以继承接口,类可以实现多个接口。
父类需要open才能被继承(非抽象类)
父类方法、属性需要open才能被复写
接口、接口方法、抽象类默认open
复写父类(接口)成员需要override关键字
abstract class A {
open fun c(){}
}
class b:A(){
override fun c() {
super.c()
println("b")
}
}
class c:A(){
override fun c() {
super.c()
println("c")
}
}
Kotlin | Java |
---|---|
Private(自己可见) | Private |
Protection(子类可见) | Protection |
- | Default(包内可见) |
Internal(模块内可见) | - |
public | public |
object只有一个实例的类
不能自定义构造方法
可以实现接口继承类
本质上就是一个单利模式
//可实现接口,定义属性,继承父类
interface a{
fun a1()
}
//父
open class fu
object obj:fu(),a{
override fun a1() {
}
var num:Int=0
}
名称相同参数列表不同
JVM函数签名:函数名称、参数列表(不包含返回值)
//重载
fun a():Int{
return 0
}
fun a(num:Int):Int{
return num
}
为函数设置一个默认值。
可以为任意位置的参数设置默认值
//默认值
fun a1(num:Int=0):Int{
return num
}
为现有的类添加扩展方法。
定义:
Fun X.y(){}
给谁定义什么方法返回什么值
//打印20次abc eg abcabc...abc
fun main(args: Array<String>) {
println("abc".a(2))
}
//给String定义一个方法a 传如一个打印次数 返回String
//从0开始一直到传入的打印次数num
//this 指代调用者
fun String.a(num:Int):String{
var stringBuilder= StringBuilder()
for (i in 0 until num){
stringBuilder.append(this)
}
return stringBuilder.toString()
}
只保存数据的类,称为数据类(data class),
为了确保自动生成的代码一致性和有意义,数据类(data class)必须满足以下要求:
主构造函数至少有一个参数。
主构造函数的所有参数需标记为val 或 var。
data class country(val id:Int,val name: String)
fun main(args: Array<String>) {
var chain =country(0,"中国")
println(chain)
val(id,name) = chain
println(id)
println(name)
}
componentN函数 结构申明
编译器为数据类(data class)自动声明componentN()函数,可直接用解构声明!
println(chain.component1())
println(chain.component2())
定义在类内部的类
与类成员有相似的访问控制
默认是静态内部类,非静态内部类加关键字inner。
定义:
class abc {
class c {
}
}
//非静态类
class abc {
var a: Int = 9
//默认为静态类 inner(非静态内部类)
inner class c {
fun hello() {
println(a) //非静态类中可访问外部值
}
}
}
//当外部类和内部类冲突时,可使用this来访问
class abc {
var a: Int = 9
//默认为静态类 inner(非静态内部类)
inner class c {
var a:Int =10
fun hello() {
println([email protected])
}
}
}
//调用
var a = abc()
println(a.c().hello())
和Java类似。
枚举也是类。
可修改构造,添加成员。
可提高代码的表现力,也有定的性能开销。
子类可数
// sealed
sealed class s {
}
class s1
class s2
v1.1之前子类必须定义为密封类的内部类。
v1.1开始子类只需与密封类为同一个文件中。
传入或者返回函数的函数。
函数引用
包级函数 ::println
引用成员方法 类名 :: 方法名,有一个隐含的参数就是调用者本身
//把数组A 的数据克隆到数组b
var list = listOf(1,2,3,23,4,2,2)
var newList = ArrayList<Int>()
//给newlist赋值
list.forEach{
newList.add(it)
}
newList.forEach(::println)
//克隆list 赋值给newList
var newList = list.map {
it
}
newList.forEach(::println)
//对遍历的数组进行操作 eg:更改数据类型
//类型转换
var newList2 = list.map(Int::toDouble)
newList.forEach(::println)
//flatMap
var list1 = listOf(
1..5,
2..40,
20..50
)
//将数组变成[1,2,..5,2...40.20..50]
val newList1 = list1.flatMap { it }
newList1.forEach(::println)
//flat 做类型转换 修改等
val newList2 = list1.flatMap {
it.map {
"No:$it"
}
}
println(newList2.forEach(::println))
//求和
println(newList1.reduce { acc, i -> acc + i })
//求阶乘
fun a(num:Int):Int{
if (num == 0) return 1
//i 1,2,2...num
return (1..num).reduce { acc, i ->acc*i }
}
//调用 map 便利 0..9 求0,1,2..9的阶乘
(0..9).map(::a).forEach(::println)
//filer 过滤
list.filter { it%2 ==0 }.forEach(::println)
//takeWhile 满足条件继续取
list.takeWhile { it==3}.forEach(::println)