适用于使用kotlin半年到2年左右的Android程序员 系统学习kotlin
区间表达式由具有操作符形式 … 的 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)
}
when类似于Java的Switch语法
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")
}
扩展:
fun hasPrefix(x: Any) = when(x) {
is String -> x.startsWith("prefix")
else -> false
}
fun main(){
doSomething(true)
doSomething(3,false)
}
private fun doSomething(age:Int = 2,flag:Boolean){
return "age:$age flag:$flag"
}
//将匿名函数赋值给一个变量
val blessingFunction:()->String = {
val holiday = "National Day."
"Happy $holiday"
}
println(blessingFunction())
和具名(有名字的)函数不一样,除了极少数情况下,匿名函数不需要return关键字返回数据,匿名函数会隐式或自动返回函数体最后一行语句的结果
和具名参数一样,匿名函数可以不带参数,也可以带一个或者多个任何类型的参数,需要带参数时,参数的类型放在匿名函数的类型定义中,参数名则放在函数定义中
val blessFunction:(String)->String = { name->
val holiday = "New Year."
"$name, Happy $holiday"
}
println(blessFunction("Jack"))
fun main(){
//隐式推断匿名函数返回值类型
val blessingFunction:()->String = {
val holiday = "National Day."
"Happy $holiday"
}
val blessingFunction = {
val holiday = "National Day."
"Happy $holiday"
}
//隐式推断匿名函数返回值参数类型
val blessFunction:(String,Int)->String = { name,year->
val holiday = "New Year."
"$name, Happy $holiday $year"
}
val blessFunction = { name:String,year:Int->
val holiday = "New Year."
"$name, Happy $holiday $year"
}
println(blessingFunction())
println(blessFunction("Jack",2027))
}
如果 一个函数的lambda参数排在最后,或者是唯一参数,那么括住lambda值参的一对圆括号就可以省略
fun main(){
val discountWords = {goodsName:String,hour:Int->
val currentYear = 2027
"${currentYear}年,双11${goodsName}促销倒计时:$hour 小时"
}
showonBoard("卫生纸",discountWords)
//简写 如果 一个函数的lambda参数排在最后,或者是唯一参数,那么括住lambda值参的一对圆括号就可以省略
showonBoard("卫生纸"){goodsName:String,hour:Int->
val currentYear = 2027
"${currentYear}年,双11${goodsName}促销倒计时:$hour 小时"
}
}
fun showonBoard(goodsName:String,showDiscount:(String,Int)->String){
//1-24区间随机数的最后一个
val hour = (1..24).shuffled().last()
println(showDiscount(goodsName,hour))
}
fun main(){
val total = "Mississippi".count()
//通过匿名函数给标准函数制定规则
val totalS = "Mississippi".count({letter->
letter == "s"
})
//简写 如果 一个函数的lambda参数排在最后,或者是唯一参数,那么括住lambda值参的一对圆括号就可以省略
val totalS = "Mississippi".count{ it == "s"}
}
lambda可以让你更灵活编写应用但是灵活也是需要付出代价的
在JVM上,定义的lambda会以对象实例的形式存在,JVM会同所有lambda打交道的 变量分配内存,这就产生了内存开销。更糟的是lambda内存开销会带来严重的性能问题,幸运的是,kotlin有一种优化机制叫做内联,有了内联,JVM不需要lambda对象实例,因而避免了变量内存分配。哪里需要使用lambda,编译器就会将函数体复制粘贴到那里
fun test() {
//多次调用 sum() 方法进行求和运算
println(sum(1, 2, 3))
println(sum(100, 200, 300))
println(sum(12, 34))
//....可能还有若干次
}
/**
* 求和计算
*/
fun sum(vararg ints: Int): Int {
var sum = 0
for (i in ints) {
sum += i
}
return sum
}
以上我们做了多此sum()方法,为了避免多次调用sum()方法带来的性能损耗,我们期望的代码类似这样子的
fun test() {
var sum = 0
for (i in arrayOf(1, 2, 3)) {
sum += i
}
println(sum)
sum = 0
for (i in arrayOf(100, 200, 300)) {
sum += i
}
println(sum)
sum = 0
for (i in arrayOf(12, 34)) {
sum += i
}
println(sum)
}
3次数据球和操作,都是在test()里面执行的,没有之前的sum()方法调用,最后的结果依然是一样的,但是由于减少了 方法的调用,虽然代码量增加了,但是性能提升了
定义内联函数
inline fun sum(vararg ints: Int): Int {
var sum = 0
for (i in ints) {
sum += i
}
return sum
}
用关键字inline标记函数,该函数就是一个内联函数,还是原来的test(),编译器会在编译时,自动把内联函数sum()方法体内的代码替换到调用该方法的地方,查看编译后的字节码,会发现test()方法里面没有了对sum()调用,凡是原来代码出现sum()方法调用的地方,出现的都是sum()方法体内的字节码了。
如果一个内联函数的参数包含lambda表达式,也就是函数参数,那么该形参也是inline的
inline fun test(inlined: () -> Unit) {...}
这里有个问题需要注意,如果在内联函数的内部,函数参数被其他非内联函数调用,就会报错
//内联函数
inline fun test(inlined: () -> Unit) {
//这里会报错
otherNoinlineMethod(inlined)
}
//非内联函数
fun otherNoinlineMethod(oninline: () -> Unit) {
}
要解决这个问题,必须为内联函数参数加上noinline修饰,表示禁止内联,保存原有函数特性
inline fun test(noinline inlined: () -> Unit) {
otherNoinlineMethod(inlined)
}
java中不能直接使用泛型的类型的,但是kotlin中确可以
inline fun <reified T: Activity> startActivity() {
startActivity(Intent(this, T::class.java))
}
使用时直接传入泛型即可,代码简洁明了
startActivity<MainActivity>()
要把函数中作为参数传给其他函数使用,除了传lambda表达式,kotlin还提供了其他方法,传递函数引用,函数引用可以把一个具名函数转换成一个值参, 使用lambda表达式的地方,都可以使用函数引用
fun main(){
/**val discountWords = {goodsName:String,hour:Int->
val currentYear = 2027
"${currentYear}年,双11${goodsName}促销倒计时:$hour 小时"
}
**/
//要获得函数引用,使用::操作符 后跟要引用的函数名
showonBoard("卫生纸",::getDiscountWords)
}
//将匿名函数更改为具名函数
fun getDiscountWords(goodsName:String,hour:Int):String{
val currentYear = 2027
"${currentYear}年,双11${goodsName}促销倒计时:$hour 小时"
}
fun showonBoard(goodsName:String,showDiscount:(String,Int)->String){
//1-24区间随机数的最后一个
val hour = (1..24).shuffled().last()
println(showDiscount(goodsName,hour))
}
一个函数返回另一个函数类型
函数类型也是有效的返回值类型,也就是说可以定义一个能返回函数的函数。
fun main(){
val getDiscountWords:(String)->String = configDiscountWords()
println(getDiscountWords("沐浴露"))
}
fun configDiscountWords():(String)->String{
val hour = (1..24).shuffled().last()
val currentYear = 2027
return {goodsName:String->
"${currentYear}年,双11${goodsName}促销倒计时:$hour 小时"
}
}
kotlin中,匿名函数能修改并引用定义在自己作用域之外的变量,匿名函数引用着定义自身的函数里的变量。kotlin中的lambda就是闭包
lambda让开发者少写模块化代码,写出更灵活的代码
Java将函数作为参数传递给另一个函数或变量(传一个函数)
public class JavaAnonymousClass{
public static void main(String[] args){
showOnBoards("牙膏",new MyDiscountWords());
//new一个匿名内部类 有实现类只不过是没有名字(有一个类继承了接口)
showOnBoards("牙膏",new DiscountWords{
@Override
public String getDiscountWords(String goodsName,int hour){
int currentYear = 2017;
return "这是一段得花文本";
}
});
}
//模块化代码
public interface DiscountWords{
String getDiscountWords(String goodsName,int hour);
}
public static void showOnBoards(String goodsName,DiscountWords discountWords){
int hour = new Random().nextInt(24);
System.out.println(discountWords.getDiscountWords(goodsName,hour));
}
static class MyDiscountWords implements DiscountWords{
@Override
public String getDiscountWords(String goodsName,int hour){
int currentYear = 2017;
return "这是一段得花文本";
}
}
}
kotlin写法
fun main(){
showonBoard("卫生纸"){goodsName:String,hour:Int->
val currentYear = 2027
"${currentYear}年,双11${goodsName}促销倒计时:$hour 小时"
}
}
fun showonBoard(goodsName:String,showDiscount:(String,Int)->String){
//1-24区间随机数的最后一个
val hour = (1..24).shuffled().last()
println(showDiscount(goodsName,hour))
}
let 返回的是最后一行的结果
fun mian(){
var str:String? = "butterfly"
str = ""
//let 返回的是最后一行的结果
str = str?.let{it->
//非空字符串
if(it.isNotBlank()){
it.capitalize()
}else{
"butterfly"
}
}
println(str)
}
fun mian(){
var number:Int? = null
try{
checkOperation(number)
number!!.plus(1)
}catche(e:Exception){
println(e)
}
fun checkOperation(number:Int?){
number?:throw UnskilledException()
checkNotNull(number,{"Something is not good."})
}
}
//自定义异常
class UnskilledException():IllegalArgumentException("操作不当")
val str1 = "The people's Republic of China."
//Regex正则匹配
val str2 = str1.replace(Regex("[aeiou]")){
when(it.value){
"a"->"7"
"e"->"6"
"i"->"9"
"o"->"1"
"u"->"3"
else-> it.value
}
}
用来检查两个字符串中字符是否匹配,用=检查两个变量是否指向内存堆上同一对象
fun mian(){
val str = "Jason"
val str2 = "Jason"
println(str==str2)->true
//java字符串有一个字符串常量池 变量值相同指向同一个内存
println(str===str2)->true
}
fun mian(){
val number1:Int? = "8.98".toIntOrNull()
println(number1)->null
println(8.56756.toInt())->9
println(8.956756.roundToInt())->8.96
val s = "%.2f".format(8.956756)
println(s)->8.96
}
fun main(){
val file1 = File("E://i have a dream_copy.txt")
file1.setReadable(true)
file1.setWriteable(true)
file1.setExecutable(false)
val file2 = File("E://i have a dearm_copy.txt").apply{this:File
setReadable(true)
setWriteable(true)
setExecutable(false)
}
}
let 返回的是最后一行的结果
fun main(){
val firstElement = listof(3,2,1).first()
val result = firstElement * firstElement
val result:Int = listOf(3,2,1).first().let{it:Int
it * it
}
println(result)
}
fun formatGetting2(guestName:String?):String{
return if(guestName !=null){
"Welcome,$it."
} ?: "What's your name?"
}
fun formatGreeting(guestName:String?):String{
return guestName?.let{it:String
"Welcome,$it."
} ?: "What's your name?"
}
run和apply差不多,但与apply不同,run函数不返回接收者,返回的是lambda结果,也就是true 或者false。不一定(run返回的是lambda表达式返回的结果/最后一行的结果)
fun main(){
val file = File("E://i have a dream_copy.txt")
val result:Boolean = file.run{this:File
readText().contains("great")
}
println(result)
val result2:Boolean = "The people's Republic of China".run(::isLong)
println(result2)
"The people's Republic of China"
.run(::isLong)
.run(showMessage)
.run(println)
}
fun isLong(name:String) = name.length >=10
fun showMessage(isLong:Boolean):String{
return if(isLong){
"Name is too long."
}else{
"Please rename."
}
}
with函数是run的变体,他们的功能行为是一样的,但是with的调用方式不同,调用时需要值参作为其第一个参数传入。
fun main(){
val result:Boolean = "The people's Republic of China".run{this:String
length >= 10
}
val result2:Boolean = with("The people's Republic of China"){this:String
length >= 10
}
}
also函数和let函数功能相视,和let一样,also也是把接受者作为值参传递给lambda,但有一点不同:also返回接受者对象,而let返回lambda结果。因为这个差异,also尤其适合 针对同一原始对象
fun main(){
var fileContents:List<String>
File("E://i have a dream_copy.txt")
.also{it:File
println(it.name)
}.also{
fileContents = it.readLines()
}
println(fileContents)
}
takeIf函数需要判断lambda中提供的条件表达式,给出true或false结果,如果判断结果是true,从takeIf函数返回接受者对象,如果false,则返回null,使用场景:需要判断某个条件是否满足,再决定是否可以赋值变量或执行某项任务
fun main(){
val result:String? = File("E://i have a dream_copy.txt")
.takeIf{ it.exists() && it.canRead() }->true返回File对象 false返回null
?.readText()
println(result)
}
takeIf辅助函数takeUnless,只有判断你给定的条件 结果是false时takeUnless才会返回原始接受者对象。
fun main(){
val fileContents3:String? = File("E://i have a dream_copy.txt")
.takeUnless{ it.isHidden //隐藏不可见 }
?.readText()
println(result)
}
fun main(){
val list:List<String> = listOf("Jason","Jack","Jacky")
println(list.getOrElse(4){"Unknown"})->找不到索引值就返回后面的
println(list.getOrNull(4))->找不到索引值就返回null
println(list.getOrNull(4) ?: "Unknown")
}
fun main(){
val mutableList:MutableList<String> = mutableListOf("Jack","Jason","Jerry")
mutableList.add("Jimmy")
mutableList.remove("Jack")
//List支持toList和toMutableList函数动态实现只读列表和可变列表的相互转换
listOf("Jason","Jack","Jacky").toMutableList()
mutableListOf("Jason","Jack","Jacky").toList()
}
能够修改可变列表的函数有个统一的名字:mutator函数
fun main(){
val mutableList:MutableList<String> = mutableListOf("Jack","Jason","Jacky")
mutableList += "Jimmy"
mutableList -= "Jason"
mutableList.removeIf{ it.contains("Jack") } //列表中如果包含Jack的全部删除
println(mutableList)->[Jimmy] //Jacky也满足条件 会被删除
}
fun main(){
val list:List<String> = listOf("Jason","jack","Jacky")
for(s in list){
println(s)
}
list.forEach{it:String
println(it)
}
list.forEachIndexed{ index,item ->
println("$index ,$item")
}
}
通过setOf创建set集合,使用elementAt函数读取集合中的元素。
set集合和list创建的集合的区别:set创建的集合不允许有重复的
fun main(){
val set:Set<String> = setOf("Jason","Jack","Jacky","Jack")
println(set.elementAt(2))
}
fun main(){
val list:List<String> = listOf("Jason","Jack","Jacky","Jack")
.toSet()
.toList()
println(list)
//快捷函数 等同于上面的效果
println(listOf("Jason","Jack","Jacky","Jack").distinct())
}
fun main(){
val map:Map<String,Int> = mapOf("Jack" to 20,"Jason" to 18,"Jack" to 30)
map(Pair("Jimmy",20),Pair("Jacky",20))
println(map["Jack"])
println(map.getValue("Jack"))
println(map.getOrElse("Rose"){"Unknow"})
println(map.getOrDefault("Rose",0))
}
fun main(){
val map:Map<String,Int> = mapOf("Jack" to 20,"Jason" to 18,"Jack" to 30)
map.forEach{it:Map.Entry<String,Int>
println("${it.key},${it.value}")
}
map.forEach{(key:String, value:Int)->
println("$key, $value")
}
//可变map
val mutableMap:MutableMap<String,Int> = mutableMapOf("Jack" to 20,"Jason" to 18,"Jack" to 30)
mutableMap += "Jimmy" to 30
mutableMap.put("Jimmy",31)
//没有就创建
mutableMap.getOrPut("Rose"){18}
println(mutableMap)
}
针对定义的每一个属性,kotlin都会产生一个field,一个getter以及一个setter,field用来存储属性数据,你不能直接定义field,kotlin会封装field,只暴露给getter和setter使用,尽管kotlin会自动提供默认getter和setter方法,但在需要控制如何读写属性数据时,你也可以定义他们
class Player{
var name:String = "jack"
//这里的field就是name值
get() = field.capitalize()
set(value){
//value传入的值
field = value.trim()
}
}
fun main(){
var p = Player()
println(p.name)
p.name = " rose "
println(p.name)
}
计算属性是通过一个覆盖的get或set运算符来定义,这时field就不需要了。
class Player{
val rolledValue
get() = (1..6).shuffled().first()
}
Player类的定义头中定义一个主构造函数,使用临时变量为Player的各个属性提供初始值,在kotlin中,为便于识别,临时变量(包括仅引用一次的参数),通常都会以 下划线开头的名字命名
class Player(
_name:String,
_age:Int,
_isNormal:Boolean
){
var name = _name
get() = field.caplize()
private set(value){//私有化setName
field = value.trim()
}
var age = _age
var isNormal = _isNormal
}
fun main(){
var player = Player("Jack",20,true)
player.name = "Rose"//这段报错
}
有主就有次,对应主构造函数的是次构造函数,我们可以定义多个次构造函数来配置不同参数组合。
class Player(
_name:String,
var age:Int,
var isNormal:Boolean
){
var name = _name
get() = field.caplize()
set(value){
field = value.trim()
}
//次构造方法也会调用主构造方法
constructor(name:String) : this(name,age=10,isNormal = false)
constructor(name:String,age:Int) : this(name,age,isNormal = false){
this.name = name.toUpperCase()
}
}
fun main(){
var player = Player("Jack",20,true)
var player2 = Player("Rose")
var player3 = Player("Jimmy",30)
}
定义构造函数时,可以给构造函数参数指定默认值,如果用户调用时不提供值参,就使用这个默认值
class Player3(
_name:String,
var age:Int = 20,
var isNormal:Boolean
){
var name = _name
get() = field.caplize()
set(value){
field = value.trim()
}
//次构造方法也会调用主构造方法
constructor(name:String) : this(name,age=10,isNormal = false)
constructor(name:String,age:Int) : this(name,age,isNormal = false){
this.name = name.toUpperCase()
}
}
fun main(){
Player3(isNormal = false,_name = "Jack")
}
初始化块可以设置变量或值,以及执行有效腥检查,如检查传给某构造函数的值是否有效,初始化块代码会在构造类实例时执行
class Player3(
_name:String,
var age:Int = 20,
var isNormal:Boolean
){
var name = _name
get() = field.caplize()
set(value){
field = value.trim()
}
//次构造方法也会调用主构造方法
constructor(name:String) : this(name,age=10,isNormal = false)
constructor(name:String,age:Int) : this(name,age,isNormal = false){
this.name = name.toUpperCase()
}
init{
//先决条件函数 require()条件不满足(为false)走后边的
require(age>0){"age must be positive"}
require(name.isNotBlank()){"Player must have a name."}
}
}
fun main(){
Player3(isNormal = false)
}
class Student(_name:String,val age:Int){
var name = _name
var score = 10
private val hobby = "music"
val subject:String
init{
println("initializing student")
subject = "math"
}
construtor(name:String):this(name,age=10){
score = 20
}
}
object ApplicationConfig{
init{
println("ApplicationConfig loading...")
}
fun doSomething(){
println("doSomething")
}
}
fun main(){
//类名,实例名
ApplicationConfig.doSomething()
//ApplicationConfig loading...
//doSomething
println(ApplicationConfig)->使用的是同一个实例
//ApplicationConfig@7ea987ac
println(ApplicationConfig)
//ApplicationConfig@7ea987ac
}
open class Player{
open fun load() = "loading nothing..."
}
fun main(){
val p = object:Player(){
override fun load() = "anoymous nothing."
}
println(p.load())->anoymous nothing.
}
object class ConfigMap{
companion object{
private const val PATH = "xxx"
fun load() = File(PATH).readBytes()
}
}
fun main(){
//无论ConfigMap实例化多少次,伴生对象(companion object)始终只有一份存在
ConfigMap.load()
}
一个类对另一个类有用,将其嵌套到该类中并使这两个类保持在一起是合乎逻辑的,可以使用嵌套类
class Player2(){
class Equipment(var name:String){
fun show() = println("equipment:$name")
}
fun battle(){
Equipment("sharp knife").show()
}
}
fun main(){
Player2.Equipment("AK47").show()
}
class Coordinate(var x:Int,var y:Int){
val isInBounds = x>0 && y>0
}
fun main(){
println(Coordinate(10,20))-> Coordinate@987
// == 比较的是内容->equals,Any 默认实现=== 比较对象引用
// ===比较的是引用
println(Coordinate(10,20)== Coordinate(10,20)) ->false
//使用data 修饰的类称为数据类 == 默认比较的是引用值
}
data class Student(var name:String,val age:Int){
private val hobby = "music"
val subject:String
var score = 0
init{
println("initializing student")
subject = "math"
}
construtor(name:String):this(name,age=10){
score = 10
}
}
fun main(){
val s = Student("Jack")
val p = s.copy()
//copy的一个弊端 不会走construtor 次构造方法里面的函数
print(s) ->Student(name="Jack",age=10,hobby="music",subject="math",score=10)
print(p)->Student(name="Jack",age=10,hobby="music",subject="math",score=0)
}
data 修饰的类默认支持结构语法
class PlayerScore(val experience:Int,val level:Int){
//设置支持结构语法
operator fun component1() = experience
operator fun component2() = level
}
fun main(){
//类默认不支持解构语法
val (x,y) = PlayerScore(10,20)
println("$x,$y")
}
enum class Direction{
EAST,
WEST,
SOUTH,
NORTH
}
fun main(){
println(Direction.EAST)->EAST
println(Direction.EAST is Direction)->true
}
enum class Direction2(private val coordinate:Coordinate){
EAST(Coordinate(1,0)),
WEST(Coordinate(-1,0)),
SOUTH(Coordinate(-1,0)),
NORTH(Coordinate(1,0)),
fun updateCoordinate(playerCoordinate:Coordinate)=
Coordinate(playerCoordinate.x+coordinate.x,playerCoordinate.y+coordinate.y)
}
fun main(){
println(Direction2.EAST.updateCoordinate(10,20))
}
enum class LicenseStatus{
UNQUALIFIED,
LEARNING,
QUALIFIED;
}
class Driver(var status:LicenseStatus){
fun checkLicense():String{
return when(status){
LicenseStatus.UNQUALIFIED->"没资格"
LicenseStatus.LEARNING->"在学"
LicenseStatus.QUALIFIED->"有资格"
}
}
}
fun main(){
println(Driver(LicenseStatus.UNQUALIFIED).checkLicense())
}
对于更复杂得ADT,可以使用kotlin的密封类(sealed class) 来实现更复杂的定义,密封类可以用来定义一个类似于枚举类的ADT,但你可以更灵活的控制他们(枚举类满足不了 )
//密封类
sealed class LicenseStatus2{
//object修饰得是单例得
object UnQualified:LicenseStatus2()
object Learning:LicenseStatus2()
class Qualified(val licenseId:String):LicenseStatus2()
}
class Driver2(var status:LicenseStatus2){
fun checkLicense():String{
return when(status){
is LicenseStatus2.UnQualified->"没资格"
is LicenseStatus2.Learning->"在学"
is LicenseStatus2.Qualified->"有资格,驾驶证编号:${(this.status as LicenseStatus2.Qualified).licenseId}"
}
}
}
fun main(){
val status = LicenseStatus2.UnQualified("2333333")
val driver = Driver2(status)
println(driver.checkLicense())
}
kotlin规定所有接口函数和属性实现都是要使用override关键字,接口中定义的函数并不需要open关键字修饰,他们默认就是open的
interface Movable{
val maxSpeed:Int
var whells:Int
fun move(movable:Movable):String
}
class Car(_name:String,override var wheels:Int = 4):Movable{
override var maxSpeed:Int
get() =
set(value){}
override fun move(movable:Movable):String{
}
}
interface Movable{
val maxSpeed:Int
get() = (0..4).shuffled().last()
var whells:Int
fun move(movable:Movable):String
}
class Car(_name:String,override var wheels:Int = 4):Movable{
override var maxSpeed:Int
get() = super.maxSpeed
set(value){}
override val maxSpeed:Int
get() = super.maxSpeed
// set(value){}
override fun move(movable:Movable):String{
}
}
class MagicBox<T>(item:T){
private var subject :T = item
}
class Boy(val name:String,val age:Int)
class Dog(val weight:Int)
fun main(){
val box1:MagicBox(Boy) = MagicBox(Boy("Jack",10))
val box2:MagicBox(Dog) = MagicBox(Dog(20))
}
class MagicBox<T>(item:T){
var available = false
private var subject :T = item
fun fetch():T?{
//takeif true返回对象 false返回null
return subject.takeIf{available}
}
fun <R> fetch(subjectModeFunction:(T)->R):R?{
return subjectModeFunction(subject).takeIf{available}
}
}
class Boy(val name:String,val age:Int)
class Dog(val weight:Int)
class Man(val name:String,val age:Int)
fun main(){
val box1:MagicBox(Boy) = MagicBox(Boy("Jack",10))
val box2:MagicBox(Dog) = MagicBox(Dog(20))
box1.available = true
box1.featch()?.run{this:Boy
println("you find $name")
}
//传入一个类型返回另外一个类型
box1.fetch{it:Boy
Man(it.name,it.age.plus(15))
}
}
class MagicBox<T:Human>(vararg item:T){
var available = false
//泛型为可变参数时,必须写out(不准确) -> 存放Human和human的子类
private var subject :Array<out T> = item
fun fetch(index:Int):T?{
//takeif true返回对象 false返回null
return subject[index].takeIf{available}
}
fun <R> fetch(index:Int,subjectModeFunction:(T)->R):R?{
return subjectModeFunction(subject[index]).takeIf{available}
}
}
open class Human(val age:Int)
class Boy(val name:String, age:Int):Human(age)
class Dog(val weight:Int)
class Man(val name:String, age:Int):Human(age)
fun main(){
val box1:MagicBox(Boy) = MagicBox(
Boy("Jack",10),
Boy("Jacky",16),
Boy("John",16)
)
box1.available = true
box1.featch()?.run{this:Boy
println("you find $name")
}
//传入一个类型返回另外一个类型
box1.fetch{it:Boy
Man(it.name,it.age.plus(15))
}
}
运算符重载
class MagicBox<T>(vararg item:T){
var available = false
//泛型为可变参数时,必须写out
private var subject :Array<out T> = item
fun fetch(index:Int):T?{
//takeif true返回对象 false返回null
return subject[index].takeIf{available}
}
fun <R> fetch(index:Int,subjectModeFunction:(T)->R):R?{
return subjectModeFunction(subject[index]).takeIf{available}
}
operator fun get(index:Int):T? = subject[index]?.takeIf{available}
}
open
class Boy(val name:String,val age:Int)
class Dog(val weight:Int)
class Man(val name:String,val age:Int)
fun main(){
val box1:MagicBox(Boy) = MagicBox(Boy("Jack",10),)
box1.available = true
box1.featch()?.run{this:Boy
println("you find $name")
}
//传入一个类型返回另外一个类型
box1.fetch{it:Boy
Man(it.name,it.age.plus(15))
}
box1[0]
}
out协变,如果泛型类只将泛型类型作为函数的返回(输出),那么使用out可以称之为生产类/接口,因为它主要是用来生产(production)指定泛型对象。
//协变
interface Production<out T>{
//只将泛型类型作为函数的返回
fun porduct():T
}
in逆变,如果泛型只将泛型类作为函数的入参(输入)那么使用in,可以称之为消费者类/接口,因为它主要是用来消费(cunsume)指定泛型对象。
interface Consumer<in T>{
fun consume(item:T)
}
invariant不变
如果泛型类即将泛型类型作为函数参数,又将泛型类型作为函数输出,那么既不用in也不用out
interface ProductionConsumer<T>{
fun product():T
fun consume(item:T)
}
汇总
//协变
interface Production<out T>{
//只将泛型类型作为函数的返回
fun porduct():T
}
//逆变
interface Consumer<in T>{
fun consume(item:T)
}
//不变
interface ProductionConsumer<T>{
fun product():T
fun consume(item:T)
}
open class Food
open class FastFood:Food()
class Burger:FastFood()
//生产者
//食品商店
class FoodStore:Production<Food>{
override fun porduct():Food{
println("Product food.")
return Food()
}
}
//快餐商店
class FastStore:Production<FastFood>{
override fun porduct():FastFood{
println("FastFood food.")
return FastFood()
}
}
//汉堡商店
class BurgerStore:Production<Burger>{
override fun porduct():Burger{
println("Burger food.")
return Burger()
}
}
//消费者
class EveryBody:Consumer<Food>{
override fun consume(item:Food){
println("Eat food.")
}
}
class ModernPeople:Consumer<FastFood>{
override fun consume(item:FastFood){
println("Eat fastFood.")
}
}
class American:Consumer<Burger>{
override fun consume(item:Burger){
println("Eat burger.")
}
}
fun main(){
//子类泛型对象可以赋值给父类泛型对象,用out 不写out关键字直接报错(同java)
val production1:Production<Food> = FoodStore()
val production2:Production<Food> = FastStore()
val production3:Production<Food> = BurgerStore()
//父类泛型对象可以赋值给子类泛型对象,用in
val consumer1:Consumer<Burger> = EveryBody()
val consumer2:Consumer<Burger> = ModernPeople()
consumer2.consume(Burger())->"Eat fastFood."
val consumer3:Consumer<Burger> = American()
}
为什么使用in&out
有时候你可能想知道某个泛型参数具体是什么类型,reified关键字能帮你检查泛型类型,kotlin不允许对泛型参数T做类型检查,因为泛型参数类型会被类型擦除,也就是说,T的类型信息在运行时是不可知的,java也有这样的规则。
class MagicBox<T:Human>(){
//随机产生一个对象,如果不是指定类型的对象,就通过backup函数生成一个指定类型的对象
//这里会报错
/** fun randomOrBackup(backup:()->T):T{
val items:List = listOf(
Boy("Jack",20),
Man("John",35)
)
val random:Human = items.shuffled().first()
return if(random is T){//这里会直接报错 因为编译时kotlin不知道T具体是什么类型 (反射机制)
random
}else{
backup()
}
} **/
//inline内联 能够提高效率 reified函数必须和inline一起使用
inline fun <reified T> randomOrBackup(backup:()->T):T{
val items:List<Human> = listOf(
Boy("Jack",20),
Man("John",35)
)
val random:Human = items.shuffled().first()
return if(random is T){//这里类型就保留下来了
random
}else{
backup()
}
}
fun <T> randomOrBackup(backup:()->T):T{
val items:List<Human> = listOf(
Boy("Jack",20),
Man("John",35)
)
val random:Human = items.shuffled().first()
return if(random is T){//这里会直接报错 因为编译时kotlin不知道T具体是什么类型 (反射机制)
random
}else{
backup()
}
}
}
open class Human(val age:Int)
class Boy(val name:String,age:Int):Human(age)
class Man(val name:String,age:Int):Human(age)
fun main(){
//val box1:MagicBox = MagicBox()
val box1:MagicBox<Man> = MagicBox()
//由backup函数,推断出T的类型
val subject:Man = box1.randomOrBackup{
Man("Jimmy",38)
}
}
扩展可以在 不修改类的定义的情况下增加类的功能扩展可以用于自定义类,也可以用于List,String,以及kotlin标准库里的其他类,和继承相似,扩展也能共享类行为,在你无法接触某个定义,或者某个类没有使用open修饰符,导致你无法继承它扩展就是增加类功能的最好选择。
//给字符串增加若干个感叹号
fun String.addExt(amount:Int=1) = this+"!".repeat(amount)
//在超类上定义扩展函数,Any所有子类都能使用该函数了
fun Any.easyPrint() = println(this)
fun main(){
println("abc".addExt(2))->"abc!!"
"test".easyPrint() ->"test"
15.easyPrint()->15
}
新的泛型扩展函数不仅可以支持任何类型的接受者,还保留了接受者的 类型信息,使用泛型类型后,扩展函数能够支持更多类型的接受者,使用范围更广了
//给字符串增加若干个感叹号
fun String.addExt(amount:Int=1) = this+"!".repeat(amount)
/**fun Any.easyPrint():Any{
println(this)
return this
} **/
fun <T> T.easyPrint():T{
println(this)
return this
}
fun main(){
//链式调用 使用注释的直接报错
"abc".easyPrint().addExt(2).easyPrint()
}
除了给类添加功能扩展函数外,可以给类定义扩展属性,给String类添加一个扩展,这个扩展属性可以统计字符串里有多少个元音字母。
val String.numVowels
get() = count{ "aeiou".contains(it) }
fun <T> T.easyPrint():T{
println(this)
return this
}
fun main(){
"The People's Republic of China".numVowels.easyPrint()
}
定义扩展函数用于可空类型,在可空类型上定义扩展函数,你就可以直接在扩展函数体内解决可能出现的空值问题。
fun String?.printDefault(default:String) = print(this ?: default)
fun main(){
val nullableString:String? = null
nullableString.printDefault("abc")
}
infix关键字适用于由单个参数的扩展类和函数,可以让你以更简洁的语法调用函数,如果一个函数定义使用了infix关键字,那么调用它时, 接收者和函数之间的点操作以及参数的一对括号都可以不要
infix fun String?.printDefault(default:String) = print(this ?: default)
fun main(){
val nullableString:String? = null
//加上infix关键字后这里可这样写
nullableString printDefault "abc"
public infix fun<A,B> A.to(that:B):Pair<A,B> = Pair(this,that)
//mapOf("jack" to 18)
//"jack".to(18)
}
扩展函数需要在多个文件里面使用,可以将它定义在单独的文件,然后import
package com.jason.kotlin.extension
//这里list/set都可以用都是它的子类
fun <T> Iterable<T>.randomTake(): T = this.shuffled().first()
import com.jaon.kotlin.extension.randomTake
import com.jaon.kotlin.extension.randomTake as randomzer
fun main(){
val list:List<String> = listOf("Jason","Jack","Tom")
val set:Set<String> = setOf("Jason","Jack","Tom")
list.randomTake()
list.randomzer()
}
//扩展函数
fun String.addExt(amount:int = 1) = this+"!".repeat(amount)
//泛型扩展函数里自带了接受者对象的this隐式调用
fun String.addExt() = "!".repeat(count())->"!".repeat(this.count())
fun <T> T.easyPrint():Unit = println(this)
//T.()->unit
public inline fun <T> T.apply(block:T.()->unit):T{
block()
return this
}
fun main(){
val file:File = File("XXX").apply{this:File
setReadable(true)
}
}
函数式编程 主要依赖于高阶函数(以函数为参数或返回函数)返回数据 这些高阶函数专用于处理各种集合,可方便地联合多个同类函数构建链式操作以创建复杂的计算行为。
map变换函数会遍历接收者集合,让变换器函数作用于集合里的各个元素,返回结果是包含已修改元素的集合,让作为链上下一个函数的输入。
fun main(){
val animals:List<String> = listOf("zebra","giraffe","elephant","rat")
val babies:List<String> = animals.map{ animal -> "A baby $animal" }
.map{ baby -> "$baby,with the cutest little tail evver!" }
//map返回的集合中元素个数和输入集合必须一样,不过返回新集合里的元素可以是不同类型的
val length:List<Int> = listOf.map{it.length}
println(animals)->["zebra","giraffe","elephant","rat"]
println(babies)->["A baby zebra","A baby giraffe","A baby elephant","rat"]
}
flatMap函数操作一个集合的集合,将其中 多个集合中的元素合并后返回一个包含所有元素的单一集合。
fun main(){
val result:List<Int> = listOf(
listOf(1,2,3),
listOf(4,5,6)
).flatMap{ it }
println(result)->[1,2,3,4,5,6]
}
过滤函数接受一个predicate函数,用它按给定条件检查接收者集合里的元素并给出true或false的判定。如果predicate函数返回true,受检元素就会添加到过滤函数返回的新集合中,如果predicate函数返回false,那么受检元素就被移出新集合。
//过滤集合中元素含有"J"字母的元素
fun main(){
val result:List<String> = listOf("Jack","Jimmy","Rose","Tom")
.filter{ it.contains("J") }
println(result)->["Jack","Jimmy"]
val items:List<List<String>> = listOf(
listOf("red apple","green apple","blue apple"),
listOf("red fish","blue fish"),
listOf("yellow banana","teal banana")
)
val readItems:List<String> = items.flatMap{ it.filter{ it.contains("red") }
println(readItems) -> ["red apple","red fish"]
}
fun main(){
//如果集合中没有元素,则返回true,否则返回false。
val mList1 = arrayListOf(0, 1, 2, 3, 4)
println(mList1.none())//false
//如果集合中没有符合匹配条件的元素,返回true,否则返回false 。
val mList2 = arrayListOf(0, 1, 2, 3, 4)
println(mList2.none { it == 5 })//true
println(mList2.none { it == 4 })//false
//除了1和它本身,不能被任何数整除的数
//取模等于0,说明能够整除,如果没有一个是等于0的,说明是素数
val numbers:List<Int> = listOf(7,4,8,4,3,22,18,11)
val primes:List<Int> = numbers.filter{number->
(2 until number).map{ number % it }
.none(it==0)
}
println(primes)->[7,3,11]
}
合并是函数式编程的第三大类函数,合并函数能 将不同的集合合并成一个新集合这和接受者是包含集合的集合的flatMap函数不同
zip合并函数来合并两个集合,返回一个包含键值对的新集合
fun main(){
val employees:List<String> = listOf("Jack","Jimmy","Jhon")
val ages:List<Int> = listOf(18,20,30)
val test:List<??> = employees.zip(ages)->[(Jack, 18), (Jimmy, 20), (Jhon, 30)]
val employeeAges:Map<String,Int> = employees.zip(ages).toMap()
println(employeeAges["Jack"])->18
}
fun main(){
//将每个元素值乘以3以后累加起来
val foldedValue:Int = listOf(1,2,3,4).fold(0){accmulator,number->
println("Accmutor value:$accmulator")->0/3/9/18
accmulator + (number *3)
}
println("Final value:$"foldedValue)->30
}
kotlin有内置惰性集合类型叫序列(Sequence),序列不会索引排序它的内容,也不记录元素数目,事实上,在使用一个序列时, 序列里的值可能有无限多因为某个数据源能产生无限多个元素。
fun Int.isPrime():Boolean{
(2 until this).map{it:Int
if(this % it == 0 ){ //说明能被整除 不是素数
return false
}
}
return true
}
fun main(){
//产生1000个素数
//假定0-500之内,可以找到1000个素数
//take(n)执行n次 执行到n时会直接提出不在执行
val totalList:List<Int> = (1..500).toList().filter{it.isPrime()}.take(1000)
println(totalList.size)->670
//从2开始一直累加
val oneTousandPrimes:Sequence<Int> = generateSequence(2){value->
value+1
}.filter{it.isPrime()}.take(1000)
println(oneTousandPrimes.toList().size)->1000
}
给kotlin类取一个别名-指定编译类的名字
Hero.kt
@file:JvmName("Hero")
fun makeProclamation() = "Greetings,beast!"
在java中调用
//没有别名状态下
HeroKt.makeProclamation()
Hero.makeProclamation()
在java中不能直接访问spells字段,所以必须调用getSpells,你可以给kotlin属性添加@JvmField注解
暴漏他的支持字段给java调用,从而避免使用getter方法
Spellbook.kt
class Spellbook{
//默认会有get/set方法
//使用JvmField修饰后mjava中就可以像在访问kotlin代码一样
@JvmField
val spells = listOf("Magic Ms.","Dartaa")
}
java中调用
public class Jhava{
Spellbook spellbook = new Spellbook();
spellbook.getSpells();
//使用JvmField修饰后
Spellbook spellbook = new Spellbook();
for(String spell:spellbook.spells){
spell
}
}
在java里,kotlin函数类型使用FunctionN这样的名字的接口来表示,FunctionN中的N代表值参的数数目(参数个数)最多23个,每个FunctionN都包含一个invoke函数
Hero.kt
//添加一个translator函数类型,接受一个字符串
//将其改为小写字母,再大写第一个字母,最后打印结果
//函数类型
val translator = {utterance:String->
println(utterance.toLowerCase().capitalize())
}
//java中调用
Function1<String,Unit> translator = Hero.getTranslator();
translator.invoke("HELLOW")->Hellow