1、方法的定义:
fun sum(a: Int, b: Int): Int { // Int 参数a和b,返回值 Int
return a + b
}
//表达式作为函数体,返回类型自动推断
fun sum(a: Int, b: Int) = a + b
public fun sum(a: Int, b: Int): Int = a + b // public 方法则必须明确写出返回类型
fun printSum(a: Int, b: Int): Unit {
print(a + b)
}
//Unit相当于java中的void
// 如果是返回 Unit类型,则可以省略(对于public方法也是这样):
public fun printSum(a: Int, b: Int) {
print(a + b)
}
可变长参数函数
fun vars(vararg v:Int){
for(vt in v){
print(vt)
}
}
// 测试
fun main(args: Array<String>) {
vars(1,2,3,4,5) // 输出12345
}
lambda(匿名函数)
// 测试
fun main(args: Array<String>) {
val sumLambda: (Int, Int) -> Int = {x,y -> x+y}
println(sumLambda(1,2)) // 输出 3
}
2、常量与变量:
var和val的区别:
val:定义的变量是不可修改的(类似于java中的final)
var:定义的变量是可以修改的
常量与变量都可以没有初始化值,但是在引用前必须初始化
编译器支持自动类型判断,即声明时可以不指定类型,由编译器判断。
val a: Int = 1
val b = 1 // 系统自动推断变量类型为Int
val c: Int // 如果不在声明时初始化则必须提供变量类型
c = 1 // 明确赋值
var x = 5 // 系统自动推断变量类型为Int
x += 1 // 变量可修改
3、NULL检查机制:
代码中可为空的参数,使用时需要进行空判断处理,2种处理方式
1、字段后加!!像Java一样抛出空异常
2、字段后加?可不做处理,返回值为 null或配合?:做空判断处理
任何对象都分为可null和不可null,当对象可以为null时,使用的时候必须加判断,或者加上!!
fun main(args: Array<String>) {
//类型后面加?表示可为空
var age: String? = "23"
//抛出空指针异常
val ages = age!!.toInt()
//不做处理返回 null
val ages1 = age?.toInt()
//age为空返回-1
val ages2 = age?.toInt() ?: -1
//-----------------------------------------------------------
//使用
val token = token()
//第一种方法
if (token != null) {//不加判断会报错
println(token.length)
//token?.length //如果token为null,则token?.length返回null,不会报错
}
//第二种方法,当token为null时,会抛出空异常
//println(token!!.length)
}
//声明返回值可以为null
fun token(): String? {
return null
}
4、类型检测及自动类型转换:
使用 is 运算符检测一个表达式是否某类型的一个实例(类似于Java中的instanceof关键字)
fun getStringLength(obj: Any): Int? {
if (obj !is String)
return null
// 在这个分支中, `obj` 的类型会被自动转换为 `String`
//---------------------------------------------------
// 在 `&&` 运算符的右侧, `obj` 的类型会被自动转换为 `String`
/* if (obj is String && obj.length > 0)
return obj.length*/
//---------------------------------------------------
return obj.length
}
5、区间:
区间表达式由具有操作符形式 .. 的 rangeTo 函数辅以 in 和 !in 形成
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)
}
6、比较数字:
Kotlin 中,三个等号 === 表示比较对象地址,两个 == 表示比较两个值大小
fun main(args: Array<String>) {
val a: Int = 10000
println(a === a) // true,值相等,对象地址相等
//经过了装箱,创建了两个不同的对象
val boxedA: Int? = a
val anotherBoxedA: Int? = a
//虽然经过了装箱,但是值是相等的,都是10000
println(boxedA === anotherBoxedA) // false,值相等,对象地址不一样
println(boxedA == anotherBoxedA) // true,值相等
}
7、类型转换:
val b: Byte = 1 // OK, 字面值是静态检测的
//al i: Int = b // 错误
val i: Int = b.toInt() // OK
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
8、数组:
数组的创建两种方式:
1、一种是使用函数arrayOf()
2、另外一种是使用工厂函数。
fun main(args: Array<String>) {
//[1,2,3]
val a = arrayOf(1, 2, 3)
//[0,2,4]
val b = Array(3, { i -> (i * 2) })
//读取数组内容
println(a[0]) // 输出结果:1
println(b[1]) // 输出结果:2
}
除了类Array,还有ByteArray, ShortArray, IntArray,用来表示各个类型的数组
9、字符串:
和 Java 一样,String 是不可变的
var str = "1234";
for (c in str) {//遍历打印字符串
println(c)
}
//----------------------------
val text = """
多行字符串
多行字符串
""".trimMargin()//去掉前置空格
10、条件控制:
fun main(args: Array<String>) {
var x = 0
if(x>0){
println("x 大于 0")
}else if(x==0){
println("x 等于 0")
}else{
println("x 小于 0")
}
var a = 1
var b = 2
val c = if (a>=b) a else b
println("c 的值为 $c")
}
使用区间
fun main(args: Array<String>) {
val x = 5
val y = 9
if (x in 1..8) {
println("x 在区间内")
}
}
11、When 表达式:
when 将它的参数和所有的分支条件顺序比较,直到某个分支满足条件。
when 既可以被当做表达式使用也可以被当做语句使用。
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // 注意这个块
print("x 不是 1 ,也不是 2")
}
}
//----------------------------------------
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
//-----------------------------------------
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")
}
//----------------------------------------------
fun hasPrefix(x: Any) = when(x) {
is String -> x.startsWith("prefix")
else -> false
}
//----------------------------------------------
//用来取代if-else if
when {
x.isOdd() -> print("x is odd")
x.isEven() -> print("x is even")
else -> print("x is funny")
}
在 when 中,else 等同 switch 的 default
12、循环控制:
For 循环
fun main(args: Array<String>) {
val items = listOf("apple", "banana", "kiwi")
for (item in items) {
println(item)
}
for (index in items.indices) {
println("item at $index is ${items[index]}")
}
}
while 与 do...while 循环
fun main(args: Array<String>) {
println("----while 使用-----")
var x = 5
while (x > 0) {
println( x--)
}
println("----do...while 使用-----")
var y = 5
do {
println(y--)
} while(y>0)
}
11、类的定义和类的属性:
//constructor...主构造函数
//如果主构造器没有任何注解,也没有任何可见度修饰符,那么constructor关键字可以省略
class Person constructor(firstName: String) {
var name: String = "张三"
}
fun main(args: Array<String>) {
var person = Person("李四")//Kotlin 中没有 new 关键字
println("姓名:" + person.name)
}
- getter 和 setter
getter 和 setter 都是可选
如果属性类型可以从初始化语句或者类的成员函数中推断出来,那就可以省去类型,val不允许设置setter函数,因为它是只读的。
var allByDefault: Int? // 错误: 需要一个初始化语句, 默认实现了 getter 和 setter 方法
var initialized = 1 // 类型为 Int, 默认实现了 getter 和 setter
val simple: Int? // 类型为 Int ,默认实现 getter ,但必须在构造函数中初始化
val inferredType = 1 // 类型为 Int 类型,默认实现 getter
示例:
class Person {
var lastName: String = "zheng"
//当调用person.lastName,获取值field.toUpperCase()
//field(后端变量) toUpperCase将变量赋值后转换为大写
get() = field.toUpperCase()
set//当调用person.lastName = "wang",触发set
var no: Int = 100
get() = field
set(value) {//value当前传递过来的值,在赋值给field(后端变量)之前做一些操作改变值
if (value < 10) {
field = value //传入的值小于10返回改值
} else {
field = -1 //如果传入的值大于等于10 返回-1
}
}
var heiht: Float = 145.4f
private set//set方法为私有是,外部不可调
}
fun main(args: Array<String>) {
var person = Person()
person.lastName = "wang"//将wang赋值给lastName
println("lastName:${person.lastName}")
person.no = 9
println("no:${person.no}")
}
- 输出
lastName:WANG
no:9
Kotlin 中类不能有字段。提供了 Backing Fields(后端变量) 机制,备用字段使用field关键字声明,field 关键词只能用于属性的访问器
- 非空属性必须在定义的时候初始化,kotlin提供了一种可以延迟初始化的方案,使用 lateinit 关键字描述属性
class MyTest {
private lateinit var person: Person
fun setUp() {
person = Person("哇哈哈")
}
fun test() {
println(person.name)
}
}
fun main(args: Array<String>) {
val myTest=MyTest()
myTest.setUp()
myTest.test()
}
- 主构造器
主构造器中不能包含任何代码,初始化代码可以放在初始化代码段中,初始化代码段使用 init 关键字作为前缀
class Person constructor(firstName: String) {
init {
println("FirstName is $firstName")
}
}
构造器有注解,或者有可见度修饰符,这时constructor关键字是必须的,注解和修饰符要放在它之前。
- 次构造函数
需要加前缀 constructor
class MyTest(name: String="") {
var strName: String = ""
var age: Int = 0
init {
strName = name
}
//同一个类中代理另一个构造函数使用 this 关键字
constructor(name: String, age: Int) : this(name) {
strName = name
this.age = age
}
fun print() {
println("strName" + strName + "-----age" + age)
}
}
fun main(args: Array<String>) {
val myTest1 = MyTest("传递给次构造函数", 20)
myTest1.print()
val myTest2 = MyTest("传递给主造函数")
myTest2.print()
}
- 输出
strName传递给次构造函数-----age20
strName传递给主造函数-----age0
如果一个非抽象类没有声明构造函数(主构造函数或次构造函数),它会产生一个没有参数的构造函数。构造函数是 public 。如果你不想你的类有公共的构造函数,你就得声明一个空的主构造函数:
class DontCreateMe private constructor () {
}
12、抽象类和嵌套类:
- 抽象类
open class Base {//open 标识此类可以被继承
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
注意:无需对抽象类或抽象成员标注open注解
- 嵌套类
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
}
13、内部类和匿名内部类:
- 内部类
class Outer {
private val bar: Int = 1
var v = "成员属性"
/**嵌套内部类**/
//类标记为inner,可以访问外部类成员:
inner class Inner {
fun foo() = bar // 访问外部类成员
fun innerTest() {
var o = this@Outer //获取外部类的对象
println("内部类可以引用外部类的成员,例如:" + o.v)
}
}
}
fun main(args: Array<String>) {
val demo = Outer().Inner().foo()
println(demo) // 1
val demo2 = Outer().Inner().innerTest()
println(demo2) // 内部类可以引用外部类的成员,例如:成员属性
}
要访问来自外部作用域的 this,我们使用this@label,其中 @label 是一个 代指 this 来源的标签
- 匿名内部类
class Test {
fun setInterFace(test: TestInterFace) {
test.test()
}
}
/**
* 定义接口
*/
interface TestInterFace {
fun test()
}
fun main(args: Array<String>) {
var test = Test()
/**
* 采用对象表达式来创建接口对象,即匿名内部类的实例。
*/
test.setInterFace(object : TestInterFace {
override fun test() {
println("对象表达式创建匿名内部类的实例")
}
})
}
14、类的修饰符:
- classModifier: 类属性修饰符,标示类本身特性。
abstract // 抽象类
final // 类不可继承,默认属性
enum // 枚举类
open // 类可继承,类默认是final的
annotation // 注解类
- accessModifier: 访问权限修饰符
private // 仅在同一个文件中可见
protected // 同一个文件中或子类可见
public // 所有调用的地方都可见
internal // 同一个模块中可见
15、继承:
Kotlin 中所有类都继承该 Any 类,它是所有类的超类,对于没有超类型声明的类是默认超类
如果一个类要被继承,可以使用 open 关键字进行修饰
open class Base(p: Int) // 定义基类
class Derived(p: Int) : Base(p)
- 子类有主构造函数
则基类必须在主构造函数中立即初始化
open class Person2(var name : String, var age : Int){// 基类
}
class Student(name : String, age : Int, var no : String, var score : Int) : Person2(name, age) {//子类
}
- 子类没有主构造函数
则必须在每一个二级构造函数中用 super 关键字初始化基类,或者在代理另一个构造函数。初始化基类时,可以调用基类的不同构造方法。
class Student : Person {
constructor(ctx: Context) : super(ctx) {
}
constructor(ctx: Context, attrs: AttributeSet) : super(ctx,attrs) {
}
}
16、重写:
在基类中,使用fun声明函数时,此函数默认为final修饰,不能被子类重写。如果允许子类重写该函数,那么就要手动添加 open 修饰它, 子类重写方法使用 override 关键词:
/**用户基类**/
open class Person{
open fun study(){ // 允许子类重写
println("我毕业了")
}
}
/**子类继承 Person 类**/
class Student : Person() {
override fun study(){ // 重写方法
println("我在读大学")
}
}
多个相同的方法(继承或者实现自其他类,如A、B类),则必须要重写该方法,使用super范型去选择性地调用父类的实现。
- 示例
open class A {
open fun f () { print("A") }
fun a() { print("a") }
}
interface B {
fun f() { print("B") } //接口的成员变量默认是 open 的
fun b() { print("b") }
}
class C() : A() , B{
override fun f() {
super<A>.f()//调用 A.f()
super<B>.f()//调用 B.f()
}
}
fun main(args: Array<String>) {
val c = C()
c.f();
}
- 输出
AB - 属性重写
使用 override 关键字,属性必须具有兼容类型,每一个声明的属性都可以通过初始化程序或者getter方法被重写:
interface Foo {
val count: Int
}
class Bar1(override val count: Int) : Foo
class Bar2 : Foo {
override var count: Int = 0
}