一、Kotlin 基础数据类型
1、定义
Kotlin是一门可以运行在java虚拟机、Android、浏览器上的静态语言与Java 100%兼容。
2、数据类型
2.1 基本数据类型
Boolean数据类型
val aBoolean: Boolean = true; // val 变量名:变量类型= 赋值
Number数据类型
val aFloat :Float =2.0F(不加F是双精度浮点型)
val minFloat :Float = -Float.Max_VALUE
0.0F/0.0F != Float.NaN 不是数
Integer :装箱类型
int : 基本类型
Kotlin:Int = 基本类型+装箱类型 合体
字符串:
char类型,一个char类型占2个字节,表示一个16位字符
转意符:
\t : 制表符
\b: 光标后退一个字符
\n: 回车换行符
\r: 光标回到行首
\' : 单引号
\": 双引号
\\ : 反斜杠
\$ : 美元符号
基本类型的转换
-val anInt : Int =5
-val aLong : Long =anInt.toLong(); //显示转换
字符串 两个整型相加母版:
println("$anInt+$anotherInt= ${anInt + anotherInt}")
美元符号
val salary : Int = 1000
print(“$$salary")
三个””” 引起来的String 通过 空格 分开进行转意
字符串比较:
-a ==b 标示比较内容,即类似于java的equals
-a ===b ,表示比较对象是否相同,是否为同一个,比较对象的引用值
2.2、类和对象
类:一个抽象的概念,具有某些特征的事物的概括,不特定指代任何一个具体的食物
class <类名> ( 构造方法) {<成员>... }
类和对象的关系
- 一个类通常可以有很多具体的对象
- 一个对象本质上只能从属于一个类
Class (1)—————对应关系——————object(1…n)
2.3 类的继承
- 提取多个类的共性得到一个更抽象的类,即是父类
- 子类拥有父类的一切特征
- 子类也可以自定义自己的特征
- 所有的类都最终继承自Any,所有类的始祖
ex,一个简单的对象及其构造方法:
class 妹子(var 性感: String,
var 长发: String,
var 甜美: String){
//若无具体实现可以删掉{ }
init {
println("new 了一个妹子, 性格:$性感,长发:$长发,甜美:$甜美")
}
}
val 我喜欢的妹子: 妹子 = 妹子("咪咪","黑色","声音")
2.4.空类型和智能类型转换
空类型安全
java:在java中null会报空指针异常
kotlin:在kotlin中任意类型都有可空和不可空两种
ex 1,判断name 是否为null的状态
old 是咧写法:
val name = xxx();
if( name == null ){
println(“xxxx”);
}else{
printlin(name.length)
}
new 示例写法:
val name = xxx():
//如果name不为空就返回长度,否则返回null
println(name?.length)
ex 2,判断name 是否为null的状态,若为null则直接返回
old 示例写法:
val name = xxx();
if(name == null ) return
new 示例写法:
//定义一个名为name的变量,它的值是xxx();如果xxx()返回的是null,就进行return——>”?:"
val name:String = xxx()?:return
“?always 表达式”:判断问号之前的表达式;如果为空,则执行?后面的语句
//将getName()的值返回并赋值给name,这个name的类型是一个不可为null的String类型
val name : String = getName() ?: return
println(name.length) //否则返回一个string类型的变量
ex 3,对于一个可null的String类型,但值却不是null
val value :String? = “FuckWorld”
if( value is String )
//在已经肯定String 字符串不为null的情况下,可以使用 “!!.” 进行表达式肯定的阐述
println(value!!.length)// !!.强制认可空类型 nullable
类型转换异常
val parent: Parent = parent()
val child:Child ?= parent as? Child//如果转换失败则抛出异常(安全的类型转换)
println( child ) //返回为null
任意类型都有可空和不可空两种:
-val notNull:String = null //错误,不能为空
-val nullable: String?= null //正确 ,可以为空 String可null类型
-notNull.length //正确 ,不为空的值可以直接使用
-nullable.length //错误, 可能为空,不能直接获取长度
-nullable !!.length //正确,强制认定nullable不可空
-nullable ?.length //正确 ,若nullable为空,返回空
智能类型转换
java style 类型转换
-val sub : SubClass = parent asSubClass
——类似于java的类型转换,失败则抛出异常
安全类型转换
-val sub:SubClass ?= parent as?SubClass
——如果转换失败,返回null 不抛异常
智能类型转换
-if(parent is SubClass)parent.<子类的成员>
——编译器尽可能的推导类型,远离无用的类型转换
-if(nullable != null) nullable.length
——正确!因为确认nullable不可空
2.5 包Package
命名空间,限定一个类的使用范围,路径
2.6 区间Range
var ranger:IntRange=0..1024//闭区间 [0,1024] ,整形区间
.. :rangeTo
var ranger_exclusive:IntRange=0 until 1024//半开区间 [0,1023] or [0,1024)
until : CloseRange {
CharRange /
ComparableRange /
IntRange
/ LongRange }
var emptyRange : IntRange = 0..-1 //表示为空区间
i in 0..100 判断 i 是否再区间 [0,100]中
range.contains(50) === 50 in range
迭代:
for ( i in ranger_exclusive ) {
print (“ $i , “)
}
2.7 数组
数组数组 ,数是数,组是组,合在一起就是组,就没有数了。
数组就是一系列的对象
基本写法:
val arrayOfInt:IntArray = intArrayOf(1 ,3 ,5 ,7)
val arrayOfChar:CharArray = charArrayOf(“i”,”a”,”m")
val arrayOfString:Array<String>=arrayOf( “me”,”is” ,”god"); //字符串数组
基本操作
1、数组的大小
arrayOfInt.size
2、 输出第i个元素:
println( arrayOf对象名 [i])输出
3、 给第i个元素赋值:
array[i] =Hello(“给第i个成员赋值”)
4、 拼接整个元素并去除其中的默认“,”
arrayOfChar .joinToString( “” ) //去除中间的,
5、字符串切割:取值array中的第2个和第3个元素
arrayOfInt .slice( 1..2 ) //传入的是一个闭区间的元素
======================================================================================
二、Kotlin 基础程序结构
1、Val和Var
val=value,值类型 ,累死Java的final修饰,不可能重复赋值
ps:类似于java的中的final ,所以一般val 修饰的词为Final类型值
val定义的是常量
val FINAL_HELLO_CHINA = “hello china” //类型推导(Java中需要必须写明final和修饰关键字)
在Java中含有final关键字修饰的变量,变量FINAL_HELLO_WORLD为编译期常量,在用到这个变量的时候,编译器一律把对这个变量的引用都会自动换成 它的值进行使用,提高代码运行效率。
public String FINAL_HELLO_WORLD =“...“;
ex:
~运行时常量;val x =getX()
~编译期常量: const val x:值类型=值
var = variable 临时 变量
ex:
var x = “HelloWorld “ //定义变量
x = “ hiWolrd “ //再次赋值
类型推导
val String = “hello” //推导出String类型
val int = 5 //Int类型
var x = getString() + 5 //String 类型
2、函数 Function
什么是函数?
以特定功能组织起来的代码块
fun[函数名](参数列表):[返回值类型] { 函数体}
fun[函数名](参数列表) = 表达式
ex:
fun sayFuck( name: String){ println(“fuck,$name”) }
fun sayFuck( name: String) = println(“fuck,$name")
什么是匿名函数?
fun(参数列表),必须赋值给一个变量否则无法识别,如上例2
值类型返回值要加参数类型 ,默认返回类型是Unit
fun main(args: Array<String>) {
val arg1 = args[0].toInt()
val arg2 = args[1].toInt()
println("$arg1+$arg2= ${sum(arg1, arg2)}")
}
fun sum(arg1: Int, arg2: Int): Int {
return arg1 + arg2
}
kotlin简便写法:
fun sum(arg1: Int, arg2: Int) = arg1 + arg2
思考:方法一定有名字嘛?
答:用一个变量接收一个方法就不需要有名字了:
也就是一个变量的值是一个函数的返回值
val int2 = fun(x:Int ):Long {
return x.toLong()
}
注意事项
- 功能单一
- 函数名顾名思义
- 参数列表不要太复杂
3、Lambda表达式 *****
本质:匿名函数(可以赋值和传递)
lambda表达式返回值 :表达式最后一行表达式的值
Lambda表达式(匿名函数)
写法:{ [参数列表]->[函数体,最后一行时返回值] }
ex:
普通函数表达式:fun sum( arg1:Int, arg2:Int ) = arg1 + arg2
lambda 表达式 : val sum ={ arg1:Int ,arg2 :Int -> arg1+arg2 }
参数列表:arg1:Int ,arg2 :Int
函数体,返回值最后一行:arg1+arg2
-> xxx :lambda表达式返回值是表达式最后一行的值
val/var xxx = {函数体} ps:arg1:Int,arg2:Int ->arg1+arg2
arg1:Int 表示参数返回类型
arg1+arg2:函数返回值
->:用右箭头链接
lambda表达式可以作为参数传递!
print(
sum(1, 2))=== print(
sum.invoke(1,3))是一个意思!—【invoke :运算符重载】
在kotlin中数组的遍历两种写法:
for( i in args) { … } //在Program arguments里: 添加参数中间用空格隔开
args.forEach( {…lambda...} )
注意:函数如果最后一个参数是lambda表达式,可以将括号内的内容移动到括号外面,若括号内无内容可直接删除。
forEach: Array的扩展方法 ,返回值是Unit类型,使用action调用传递的Array里面的元素作为参数
Array<out T> .forEach (action : (T) -> Unit ) :Unit {for (element in this ) action (element) }
action : (T):参数的类型
->Unit :lambda 表达式返回的类型
(action : (T) -> Unit )==lambda
args.forEach( {println(it)} ) === args.forEach(){ println(it) }====>
注意:若参数的最后一个是lambda表达式,可以将( ) 移动到lambda表达式外面
args.forEach(::println) //[若传入的函数和lambda表达式类型完全一样]
将println函数作为参数传递 ,println的参数是Any,接收任何对象都可以
args.forEachForEach@{
if(i=x) return@ForEach
println(it)
}
println(...)
终断lambda表达式的迭代。以上做法可以跳出lambda表达式内的执行继续执行lambda表达式下面的内容
@ForEach:接收Array<T>类型
Lambda表达式的类型举例
main(args:Array<String>) 函数
—— 传入参数 Array<String> -> Unit
println(…)
——传入(Any?可空的any)-> 返回Unit
()->Unit
——无参,返回值Unit
(Int)->Int
——传入整型,返回一个整型
(String,(String) ->String)->Boolean
——传入字符串,lambda表达式,返回一个boolean
println(::printUsage is () -> Unit) :参数为0的Function0 ()————具名函数printUsage()
invoke多少参数就是Function几,最多可以Function0-22
lambda表达式的调用
- 用()进行调用
- 等价于invoke()
ex : val sum = {a:Int ,b:int -> a+b}
// in P1,in P2 out R ,传入两个int返回一个int ,Function0-22均调用invoke(参数列表)
sum(2,3) ——————> sum.invoke(2,3)
lambda表达式的简化特点
- 函数参数调用时最后一个lambda表达式可以移出去
- 函数参数只有一个lambda,调用时小括号可以省略
- lambda只有一个参数可默认为it
- 入参、返回值与形参一致的函数可以用函数引用的方式作为实参传入(forEach里面传入一个printnln [::println])
3、类的成员
ex:
public class JavaA {
private int b = 0;
public int getB() {
System.out.println("some one tyies to get b");
return b;
}
public void setB(int b) {
System.out.println("some one tyies to set b");
this.b = b;
}
}
class A {
在kotlin方法中get()、set()是默认实现的版本,想要实现必须得复写get or set方法
var b = 0;
get() {
println("...")
return field//field 这里指代b后面真正的值,只在get/set访问器中才能访问到
}
set(value){
field = value ; //相当于java中的this .b=b;
}
}
lateinit var c:String
延时初始化,在需要的时候进行初始化,禁止初始化之前使用
lateinit val e:x is false
var 可以使用 lateinit 进行延时初始化,也只能使用在var上 ,但val的延时初始化需要用下面的操作:
val e : X by lazy {
… //适合于val的懒加载模式
}
当var xx:String ?= null //初始化为null并且可以是为null的类型
在使用的时候不可使用,因为没有向编译器保证该值不能为null
解决办法:
println(cc?.length) or println(cc!!.length)
类成员
属性:或者说成员变量,类范围内的变量
方法:或者说成员函数,类范围内的函数
构造方法 参数中 val / var 修饰的都是属性,没有就只是构造方法的参数
0、val 没有set()方法因为是不可变的final的
1、 属性的初始化尽量在构造方法中完成。
2、无法在构造方法中完成初始化,尝试降级为局部变量
3、var用lateinit延迟初始化,val用lazy
4、可空类型谨慎用null直接初始化 (!!or?)
4、基本运算符
- 运算符方法重载的定义,任何类可以定义或是重载基本运算符
- 通过运算符对应的具名函数来定义,要求方法名字必须一致对的上,fun前面加上operator 关键字
- 参数的个数对的上,类型和返回类型不作要求
- 不能像Scala一样定义任意运算符
加法:
class Complex(var real: Double, var imaginary: Double) {
//定义一个加法函数
operator fun plus(other: Complex): Complex {
return Complex(real +other.real, imaginary + other.imaginary)
}
operator fun plus(other: Int): Complex {
return Complex(real + other, imaginary)
}
override fun toString(): String {
return "$real+${imaginary} i"//实部+虚部
}
//取模方法
operator fun invoke() :Double{
return Math.hypot(real, imaginary)
}
class Book {
//中缀表达式 infix
infix fun on(any: Any): Boolean {
return false
}
}
fun main(args: Array<String>) {
val c1 = Complex(3.0, 4.0) //3+4i
val c2 = Complex(2.0, 7.5) //2+7.5i
println(c1 + c2) //5+11.5i—>toString() 需要用到
println(c1 + 4)
println(c1) //(3的平方+4的平方) 再开方
//-name <Name> //args contains name,如果包含的话就取后面的Name
if ("-name" in args) { //in表示Array<out T>.contains(element: T): Boolean,
// 实际上返回的是indexOf >=0,如果indexOf有这个元素,返回的就是它的位置如果没有返回 -1
println(args[args.indexOf("-name") + 1])//返回-name在args里面的后面一个位置的值
}
if(Book() on Desk()) { … }//DSL里常见 中缀表达式替换.()方法
}
}
5、表达式 -中缀表达式、分支表达式、when表达式
5.1、中缀表达式
只有一个参数,且用infix修饰的函数
ex:
class Book {
infix fun on ( place :String){
…}
}
Book() on “My desk”()
5.2、 分支表达式
kotlin 中的分支表达式:
- if表达式 :
if…else :
表达式与完备性
val x = if (b <0) 0 else b
val x = if (b <0) 0//错误 ,赋值时,分支必须完备
val mode = if (args.isNotEmpty() && args[0] == "1") {
//if表达式有返回值,每个分支里面的最后一句话
DEBUG
} else {
USER
}
when表达式
- when表达式 :
加强版 switch,支持任意类型,不用写break
支持纯表达式条件分支 类型if
表达式与完备性
fun pause() {
when (state) {
Player.State.BUFFERING -> {}
Player.State.PAUSED -> {}
Player.State.PLAYING -> {}
}
}
fun main(args: Array<String>) {
val x = 5
when (x) {
is Int -> println("Hello $x")
in 1..100 -> println("$x is in 1..100")
!in 1..100 -> println("$x is not in 1..100")
args[0].toInt() -> println("x == args[0")
}
}
6、循环表达式
6.1 for循环实例: for(element in elements)...
A:
for (arg in args) {
println(arg)
}
B:
for ((index, value) in args.withIndex()) {
println("$index->$value")
//0 -> a 1-> b 2-> c 3-> d
}
C:结果和B一模一样
for (indexedValue in args.withIndex()) {
println("${indexedValue.index}->${indexedValue.value}”)
//0 -> a 1-> b 2-> c 3-> d
}
解析:返回一个IndexValue 类型
public fun <T> Array<out T>.withIndex(): Iterable<IndexedValue<T>> {
return IndexingIterable { iterator() }
}
public data class IndexedValue<out T>(public val index: Int, public val value: T)
6.2 while循环实例:
while(…) / do {...} while(…)...
var x = 5
//1、首先判断条件,接着执行循环体
while (x > 0) {
println(x)
x--
}
//2、先执行一次循环体,再判断条件是否继续执行
do {
println()
x--
} while (x > 0)
6.3 continue、break 跳出循环实例:跳过当前循环 continue ;终止循环 用break
class Student {
fun isNotClothedProperly(): Boolean {
return false
}
fun main(args: Array<String>) {
val you = Student()
val students = ArrayList<Student>()
for (students in students) {
if (students == you) continue //当条件满足时候 ,跳过循环
if (students.isNotClothedProperly()) {
break//当条件满足时候,跳出循环
}
}
}
}
多层循环嵌套的终止结合标签使用:
Outter@for (…) {
Inner@while ( i < 0) {
if( …)
// break :若没有@Outter 这里是把while循环给break掉,继续执行for循环
break@Outter // 将for循环也给break 掉
}
}
课后小栗子:
自定义一个迭代器,实现数组的添加or 删除功能
fun main(args: Array<String>) {
val list=MyIntList()
list.add(1)
list.add(2)
list.add(3)
}
class MyIterator(val iterator: Iterator<Int>) {
operator fun next(): Int {
return iterator.next()
}
operator fun hasNext(): Boolean {
return iterator.hasNext()
}
}
class MyIntList {
private val list = ArrayList<Int>()
fun add(int: Int) {
list.add(int)
}
fun remove(int: Int) {
list.remove(int)
}
//定义operator 运算符
operator fun iterator(): MyIterator {
return MyIterator(list.iterator())
}
}
7、异常捕获
try{…}catch(e:xxxx){...}finally{…} 表达式,可以用来赋值
val result = try{ … }catch(e:xxx){ … }
注意:
先执行finally 代码在返回 return
return try { x/y
}catch (e: Exception ){
0
}finally{
…
}
8、具名参数、变长参数、默认参数
具名参数:指定实参的方式 ,在使用的时候需要赋值在括号里(...)
fun sum( arg1: Int ,arg2: Int ) =arg1+arg2
sum ( arg1 = 2 ,arg2 =3) //参数位置可以互换
变长参数:某个参数可以接受多个值;可以不为最后一个参数;参数时候有歧义需要使用具名参数
args:Array<String> —> varargargs:String 两者完全一样
hello(1, 2, 3, 4, 5, string = "hello”)
//将hello赋值给string,在这里使用具名参数,将1-5赋值给ints,后面复制给string
ex:
fun hello(vararg ints: Int, string: String) {
ints.forEach(::println)
println(string)
}
Spread Operator——变长参数场景
特点:
-
*array :表示把array展开(只支持array数组),变成一个个元素传入使用
- 该类仅支持变长参数的实参
- 不能重载
- 只能使用array数组 ,不能使用list 等其他集合
val array = intArrayOf (1 ,2 ,3,4)
hello( 3.0, *array ,string = “hello”)
默认参数:可以指定给参数列表的任意一个参数,如果把默认参数指定给一个比较靠前位置的参数,其后面的参数需要使用具名参数的形式表示
一个简单的命令行计算器:
/**
* 一个极简的命令行计数器
* */
fun main(args: Array<String>) {
while (true) {
try {
println("请输入算式数据,比如:1+1")
val input = readLine() ?: break
val splits = input.trim().split(" “) //空格分隔符,并去掉string首尾空字符
if (splits.size < 3) {
throw IllegalArgumentException("参数个数异常")
}
val arg1 = splits[0].toDouble()
val op = splits[1] //运算符
val arg2 = splits[2].toDouble()
println(
"$arg1 $op $arg2=${
Operator(op).invoke(arg1, arg2)
}”
) //将op运算符赋值给等式
} catch (e: NumberFormatException) {
println("类型格式异常:" + e)
} catch (e: IllegalArgumentException) {
println("输入是否为三个参数的一般计算:" + e)
}
println("是否再来一次[Y]")
val cmd = readLine()
if (cmd == null || cmd.toLowerCase() == "y") {
println("输入有误请查看")
break
}
}
}
//定义一个运算类
class Operator(op: String) {
val opFun: (left: Double, right: Double) -> Double
//初始化opFun
init {
opFun = when(op) {
"+" -> { l, r -> l + r }
"-" -> { l, r -> l - r }
"*" -> { l, r -> l * r }
"/" -> { l, r -> l / r }
"%" -> { l, r -> l % r }
else -> {//when表达式的完备性
throw UnsupportedOperationException(op)
}
}
}
fun invoke(left: Double, right: Double): Double {
return opFun(left, right)
}
}
Kotlin导出执行程序
在根目录 ,build.gradle中添加依赖:
apply plugin: ‘application'
mainClassName = “包名.类名kt”
CMD: cd build/instal/下包名
授予权限:chmod 755 bin/下包名
bin/下包名
====================================================================================================
三、Kotlin 基础面向对象
1、面向对象
一种程序设计的思路、思想、方法。
抽象类和接口
在 java中 :
if( A ==true ){….}
在kotlin中:
is A ->{ ….}
在kotlin中一个类可以实现多个接口。同Java,单继承多实现!
在 kotlin中可以对接口方法进行默认实现!但是不能含有状态:
ex:
抽象类: abstract class A{——具体类的特征
var i = 0;
open fun hello(){
println( i )
}
}
接口方法: interface B{
var j: Int
fun hello(){
print( j ) ——默认实现
}
}
接口 实现类: class D(override var j:Int):B{ //继承B
//若有默认实现则可以不用在这里实现,若无默认实现则必须复写
}
抽象类 实现类: class E :A( ){ //继承抽象类(...)需要实现其构造方法
override fun hello (){}
}
抽象类反映的是事务的本质(一类型的概括),接口反映的是事务的能力(一类事物内的一种能力)。
东西的中心词前面的修饰词 就是接口要负责的部分,中心词就是抽象类要负责的部分。
ex: //<外星人><笔记本> 电脑
面向对象描述: interface 外星人{….},笔记本 {...} abstract class 电脑 {….}
小结:
- 接口 ,直接理解就是一种约定/协议
- 接口不能有状态
- 接口必须由类对其进行实现后使用
- 抽象类是实现了一部分协议的半成品
- 抽象类可以有状态,可以有方法实现
- 抽象类必须由子类继承后使用
共性:
- 比较抽象,不能直接实例化
- 是需要有子类(实现类)实现的方法
- 父类(接口)变量可以接受子类(实现类)的实例赋值
区别:
- 抽象类有状态,接口没有状态
-
抽象类有方法实现,接口只能无状态的默认实现
- 抽象类只能单继承,接口可以多实现
-
抽象类反映本质,接口体现能力
1.2 继承
不是abstract类又想被继承,必须用 open关键字修饰目标类
一个类想要被继承必须先open,一个方法想要被继承也需要先open,abstract的类和方法(需要override关键字修饰)不需要open关键字修饰
- 在kotlin中,默认的类和方法都是final的,如果想要被继承,请open
- 复写一个方法必须添加 override 关键字
- 方法的属性依然可以被复写:
ex:
abstract class Person ( open val arg : Int ) {
…….
}
class Dr ( age : Int ) : Person (age){
override val age :Int
get() = …
override …{}
}
接口代理:
接口名称 by 接口方法 ,不需要方法实现 —> 不需要在方法内复写接口的实现
等同于 override fun 接口方法( ){…}
by :关键字 接口代理、属性代理
区别于普通情况:一个类实现一个接口,需要在类里实现接口里的方法,by修饰的方法不需要实现
//解决签名完全相同的两个不同接口中的方法造成的冲突
abstract class A {
open fun x(): Int = 5
}
interface E {
fun x(): Int = 1
}
interface F {
fun x(): Int = 0
}
class G(var y: Int = 0) : A(), E, F {
override fun x(): Int {
println("call x() :Int in G")
if (y > 0) {
return y
} else if (y < -300) {
return super<F>.x()
} else if (y < -100) {
return super<E>.x()
} else {
return super<A>.x()
}
}
}
fun main(args: Array<String>) {
println(G(3).x())
println(G(-110).x())
println(G(-1000010).x())
}
小总结:
继承语法要点:
- 父类需要open才可以被继承
- 父类方法、属性需要open才可以被复写
- kotlin默认都是final修饰的
- 复写父类(接口)成员需要override关键字
- class D:A(),B,C
- 注意继承类是实际上调用了父类构造方法
- 类只能单继承,接口可以多实现
接口代理回顾
- class Manager (driver :Driver ): Driver by driver
- 接口方法实现交给代理类实现
接口方法冲突回顾
- 接口方法可以有默认实现
- 签名一致且返回值相同的冲突
- 子类(实现类)必须复写冲突方法
- super<[父类(接口)名]>.[方法名]([参数列表])
1.3 类的可见性-封装
java 默认修饰类型是:default(包内可见,你懂的)
kotlin 默认修饰类型是:public
C++默认修饰类型是:private
internal 修饰:模块类可见
1.4 Object kotlin
在kotlin实现单例模式 最好的方式就是object的单例类
在java代码中,通过类.INSTANCE.方法()就能实现对类的方法的引用
- 只有一个实例的类
- 并且不能自定义构造方法
- 可以实现接口、继承父类
- 本质上就是单例模式最基本的实现
1.5 伴生对象与静态成员
- 每个类可以对应一个伴生对象
- 伴生对象的成员全局独一份
- 伴生对象的成员类似于Java的静态成员
- 静态成员考虑用包级函数、变量替代
- @JvmField 和 @JvmStatic
包级函数
在kotlin中,针对使用静态方法的场景,请尽量考虑使用包级的函数
包级函数:并没有存在于某一个类当中
kotlin中的类方法:
class Latitudeprivate constructor (val value :Double){
//提供一种方法拿到latitude的实例
companion object{
//静态方法 ,类似于Integer的静态方法 valueOf
fun ofDouble(double:Double):Latitude{
return Latitude(double)
}
val TAG:String = “Latitude"
}
}
companion object{…}这个类的伴生对象
定义一个类xxx,同时会对应一个object对象,这个object只有一个实例
在Kotlin 的main函数中的使用:
val latitude = Latitude.ofDouble(23.0)
Latitude.TAG
在Java 中的拿到kotlin中类的实例:
//拿到Companion object的对象(object里面的instance) 也就是拿到了Latitude类的对象
Latitude latitude = Latitude .Companion .ofDouble (22.0)
当在kotlin方法中加入了@JvmStatic /@JvmField注解时:
class Latitudeprivate constructor (val value :Double){
//提供一种方法拿到latitude的实例
companion object{
@JvmStatic
fun ofDouble(double:Double):Latitude{//静态方法 ,类似于Integer的静态方法 valueOf
return Latitude(double)
}
@JvmField //静态变量
val TAG:String = “Latitude"
}
}
在Java中的使用可以简化为:
Latitude latitude =Latitude.ofDouble (22.0)
Latitude.TAG
1.6 方法重载与默认参数
复写:重写了父类已经存在的方法
重载:与已经存在的某个方法名相同、参数不同
避免定义关系不大的重载方法
引起不必要的bug
- 为函数参数设定一个默认值
- 可以为任意位置的参数设置默认值
- 函数调用产生混淆时用具名参数
ps: 返回值类型不影响方法签名,在koltin中推荐通过默认参数的方法替代方法重载的使用
ex:
在Java中 remove list中第i 的元素
在kotlin中 removeAt list中第 i的元素
在Java方法中的调用:
@JvmOverloads
fun xx( xx: xx ): xx{ … }
1.4 扩展成员
1.4.1 扩展方法:this指代它的调用者,这里就是指代abc
在java中的调用:
类名kt.方法名(receiver扩展对象,参数)
ex:
fun main( args: Array<String>){
//不带operator 运算符重载的情况:
println("abc “.times(16) )
//带operator 运算符重载的情况:
println (“ bcd” * 15 )
}
//@param:int次数
//定义一个运算符重载的扩展方法
operatorfun String.times(int:Int):String{ //int 重复的次数
val stringBuilder = StringBuilder()
for ( i in 0 until int){ //从0一直到传入的重复次数(0~ int-1次) .. :int+1 次
stringBuilder.append(this)
}
return stringBuilder.toString()
}
1.4.2 扩展属性:
ex:
val String.a:String
get() = “abc”
“字符串”.a 属性 //相当于调用了get()方法
val String.b :Int
set(value){}
get() = 5
赋值使用:
“abc”.a = “ssds"
“abc”.b =10
小结:
为现有的类添加方法、属性:
fun X.y(): z {…}
val X.m 注意扩展属性不能初始化,类型接口属性
Java调用扩展成员类似调用静态方案
1.5 属性代理
定义方法:
val /var <property name(傀儡) >: <Type(类型,可以推导)> by
<expression> //返回代理对象
代理者需要实现相应的setValue/getValue 对象方法 ;
ps:val只需要实现getValue,var都需要实现
val fuck bylazy{ //并不是在初始化的时候赋值,而是通过lazy代理初始化
“fuck world” //注意:再第一次访问fuck的时候给fuck赋值为fuck world
}
ps:要求val修饰的对象,代理对象必须有一个getValue()的方法;也就是说
val fuck2 by X()
一但用by,delegate为变量初始化,或是说一旦通过这种方法声明一个变量:
其get()方法就交给其代理对象X()来操作,也就说需要再X类中 进行getValue(),setValue()如下:
ex2:
var fuck3 by X()//x代理fuck3,相当于fuck3是一个傀儡它的真实的值是value
class X{
private var value:String?=null
operator fun getValue(thisRef:Any?,property:Property<*>):String{
println (“ getValue:$thisRef ->${property.name} ")
return value?: “” //如果为null 返回空字符串
}
operator fun setValue(thisRef:Any?,property:Property<*>,value :String){
this .value = value //this.value 接收外部的value的值
}
}
1.6 数据类
data class类名(val 参数1, val 参数2, ...)
这样的方法:无需手动创建访问器:自带getter/setter访问器及toString()方法,也就是说默认实现copy\toString等方法
val 对象 = 类(参数...)
对象.component1~X() : 输出第x个元素值
val(参数1,参数2, ...) = data class
在这里参数1 =对象.component1
参数2 = 对象.component2
...
for( (index ,value )in args .withIndex()){
//withIndex()返回了一个IndexedValue的迭代器,IndexedValue()本身是一个data class;
含有两个参数val index:Int 和val value:T,实际上就是component1、2
}
因为data class从设计角度来说不能有子类,所以:
ps:data class 本身反编译是一个final class 修饰的方法,默认没有无参数的构造方法。
JavaBean必须有无参的构造方法,而且通常可以被继承!!!最好不直接使用data class 充当JavaBean;
data class 初始化需要给属性赋值
为了解决以上的问题,官方提供了:
在工程的Project:build文件中 添加allOpen 和noArg 插件:
apply plugin: 'application'
apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'kotlin-noarg'
apply plugin: 'kotlin-allopen’
//配置 :如果该类被标注为以下两种状态,在编译的时候会把类的final给去掉,同时生成一个默认的无参构造方法
noArg{
annotation(“包名.annotations.xxx(类名)")
}
allOpen{
annotation("包名.annotations.xxx(类名)")
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
//添加noArg 和 AllOpen kotlin 插件:
classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
}
在需要使用的类前添加注解 :@类名 ~比如 @Poky
1.7 内部类
1.7.1、在Java中的内部类: 默认的类是非静态内部类
ps:区别 是否持有外部类的状态,静态内部类无法持有外部类的引用(状态)
public class JavaA {
private int a;
public class Inner{ //Inner非静态内部类,持有外部JavaA类的引用(状态)
//(1)public staticclass Inner{
// public void fuck(){
// System.out.println(JavaA.this.a)} }
}
public static void main(String args){
JavaA javaa = new JavaA(); //定义一个外部类的实例
// javaa.new 是实例的方法而不是一个类方法,换句话说是一个持有状态的 而不是静态的
Inner inner =javaa.new Inner(); //通过外部类的实例创建新的内部类实例
}
}
思考:在(1)中是否能打印a?
答:不可以,需要去掉static关键字。因为非静态内部类是持有外部类的引用(状态),在fuck()方法中可以访问到Java.this
1.7.2、在kotlin中的内部类:默认的类是静态内部类
非静态内部类的写法:添加inner 关键字
class Outter{
inner class Inner{
println([email protected])
}
}
val inner = Outter().Inner() //实例化
ps:注意 println() 函数里的a变量,引用的是外部类的临时变量a的全称是 this@Outter. a
静态内部类和非静态内部类的使用
- 内部类的实例必须依赖外部类的实例,这时候可以使用非静态内部类。
- 如果内部类只是逻辑上与外部类有关联,这个内部类实例的存在并不依赖外部类这时候可以使用静态内部类
1.7.3、匿名内部类—编译期生成类似Outter$1.class
- 在kotlin中可继承1个类,实现多个接口
-
在java中要么实现1个接口,要么1个类
在kotlin中的匿名内部类:类名在编译时会生成,类似类名$1.class 的名字
interface OnClikListener {
fun OnClick()
}
class View {
var onClickListener: OnClickListener? = null //一个可空的listener
}
fun main(args: Array<String>) {
val inner = Outter().Inner()
//注册onClickListener事件
val view = View()
view.onClickListener = object : (父类()),OnClickListener {//匿名内部类
override fun OnClick() {
}
}
}
1.7 枚举
- 元素有限可数的类型的类
- 可以修改构造,添加成员
排序:$ordinal
枚举需要把元素和其他分割开 用 “;”
1.8 密封类 sealed Class
什么是密封类?
子类有限的类。实例可数,子类可数。
sealed class 类名{...}
ps:sealed class的子类只能定义在与sealed class 同一个文件当中,或是其内部类。
枚举:不需要参数,一个实例就可以表达意思 ——实例可数
密封:指令的方式 ,普通的class 再其他类使用的时候可以继承再使用,而sealed class可以有效的保护指令集不被继承篡改