Kotlin基础学习笔记 上

一、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可以有效的保护指令集不被继承篡改
 
 

猜你喜欢

转载自www.cnblogs.com/cold-ice/p/9472719.html