Kotlin笔记小结(For Java Developer)
这篇文章为kotlin学习记录,主要针对的是自己的知识盲区,不适用于新手。
文中所有demo均来自于kotlin官网
类型
整形
| Type | Size (bits) | Min value | Max value |
|---|---|---|---|
| Byte | 8 | -128 | 127 |
| Short | 16 | -32768 | 32767 |
| Int | 32 | -2,147,483,648 (-231) | 2,147,483,647 (231 - 1) |
| Long | 64 | -9,223,372,036,854,775,808 (-263) | 9,223,372,036,854,775,807 (263 - 1) |
浮点型
| Type | Size (bits) | Significant bits | Exponent bits | Decimal digits |
|---|---|---|---|---|
| Float | 32 | 24 | 8 | 6-7 |
| Double | 64 | 53 | 11 | 15-16 |
变量
只读变量使用val,可以被重新赋值的变量使用关键字var
// example of val
val a: Int = 1 // immediate assignment
val b = 2 // `Int` type is inferred
val c: Int // Type required when no initializer is provided
c = 3 // deferred assignment
// example of var
var x = 5 // `Int` type is inferred
x += 1
函数
默认参数
fun foo(a: Int = 0, b: String = "") { ... }
函数扩展
fun String.spaceToCamelCase() { ... }
"Convert this to camelcase".spaceToCamelCase()
单行表达式函数
fun theAnswer() = 42
// 同样适用于when
fun transform(color: String): Int = when (color) {
"Red" -> 0
"Green" -> 1
"Blue" -> 2
else -> throw IllegalArgumentException("Invalid color param value")
}
Lambda表达式
匿名函数
带receiver的函数字面量
类似于函数扩展,使得你能够直接在函数体中直接返回到receiver object. 在函数体重,receiver object使用this访问到,你可以通过this来获取到reciver object中的属性和方法。
val sum: Int.(Int) -> Int = { other -> plus(other)
// 匿名函数
val sum = fun Int.(other: Int): Int = this + other
Lambda表达式
基本语法
val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
- lambda 使用
{}包围 - 在
→后面的为函数体 - 如果lambda的返回值不为空(
Unit),那么最后一个表达式即为返回值,上面的例子中,x + y即为返回值
类型声明可以省略,简略写法为:
val sum = { x: Int, y: Int -> x + y }
lambda为函数的最后一个参数
如果lambda为函数的最后一个参数,那么可以直接简写在函数外面,例如
val product = items.fold(1) { acc, e -> acc * e }
// 如果lambda作为函数的最后一个参数,那么,函数()可以省略
run { println("...") }
隐式的it
如果lambda只有一个参数,那么这个参数可以不必要声明,参数会隐式被it代替
ints.filter { it > 0 } // this literal is of type '(it: Int) -> Boolean'
// 等价于
ints.filter { intVal -> intVal > 0 }
lambda返回值
默认如果lambda的返回值不为空(Unit),那么最后一个表达式即为返回值,但是也可以显示地使用return来进行值的返回,但是要遵循qualified return语法
ints.filter {
val shouldFilter = it > 0
shouldFilter
}
ints.filter {
val shouldFilter = it > 0
return@filter shouldFilter
}
跳过lambda的返回值
如果不需要使用到lambda的参数,那么可以使用_代替
map.forEach { _, value -> println("$value!") }
匿名函数
匿名函数和lambda表达式一样,都是函数字面量。
Function literals are functions that are not declared but are passed immediately as an expression.
fun(x: Int, y: Int): Int = x + y
和lambda相比,匿名函数主要有两点不同
- 匿名函数可以指定返回类型,而lambda不同
- 如果没有定义返回标签,lambda的返回会直接返回到最近定义了
fun关键字的函数,而匿名函数会返回到函数本身
类
类的继承使用:,类默认是final类型的(即不可修改),如果要让类可继承,使用open关键字
open class Shape
class Rectangle(var height: Double, var length: Double): Shape() {
var perimeter = (height + length) * 2
}
DTOs(POJOs/POCOs)
data class主要用作承载数据的对象,可以用作两个系统之间数据的交换
data class Customer(val name: String, val email: String)
- getters (and setters in case of
vars) for all properties - equals()
- hashCode()
- toString()
- copy()
- component1(), component2() 用作数据解构
实例化一个抽象类
abstract class MyAbstractClass {
abstract fun doSomething()
abstract fun sleep()
}
fun main() {
val myObject = object : MyAbstractClass() {
override fun doSomething() {
// ...
}
override fun sleep() { // ...
}
}
myObject.doSomething()
}
匿名类
Object表达式 主要用来创建匿名类
val helloWorld = object {
val hello = "Hello"
val world = "World"
// object expressions extend Any, so `override` is required on `toString()`
override fun toString() = "$hello $world"
}
匿名内部类
匿名内部类也叫伴生类,使用关键字companion,通常的作用是为类提供静态属性和方法,类似于java的static关键字
// 一个类中只允许声明一个伴生类
// 下面的例子中,如果同时存在两个伴生类,会报类型错误
class MyClass {
// 也可以声明名字
companion object Factory {
fun create(): MyClass = MyClass()
}
// 匿名, Companion
companion object {
fun create(): MyClass = MyClass()
}
}
fun main() {
// 调用可以省略掉伴生类名
println(MyClass.create())
println(MyClass.Factory.create())
println(MyClass.Companion.create())
}
字符串模板
var a = 1
// simple name in template:
val s1 = "a is $a"
a = 2
// 如果需要计算,则使用${xxx}来表示
// arbitrary expression in template:
val s2 = "${s1.replace("is", "was")}, but now is $a"
条件判断
条件判断采用关键字if / else if / else,需要注意一点的是if也可以作为表达式使用
fun maxOf(a: Int, b: Int) = if (a > b) a else b
if-not-null-else 简写
val files = File("Test").listFiles()
println(files?.size ?: "empty") // if files is null, this prints "empty"
// To calculate the fallback value in a code block, use `run`
val filesSize = files?.size ?: run {
return someSize
}
println(filesSize)
if not null 执行 (let)
val value = ...
value?.let {
... // execute this block if not null
}
Break and continue 标签
loop@ for (i in 1..100) {
for (j in 1..100) {
if (...) break@loop
}
}
Return to 自定义标签
the
return-expression returns from the nearest enclosing function
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return // non-local return directly to the caller of foo()
print(it)
}
println("this point is unreachable")
}
// To return from a lambda expression, label it and qualify the return:
fun foo() {
listOf(1, 2, 3, 4, 5).forEach lit@{
if (it == 3) return@lit // local return to the caller of the lambda - the forEach loop
print(it)
}
print(" done with explicit label")
}
// Often it is more convenient to use implicit labels
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@forEach // local return to the caller of the lambda - the forEach loop
print(it)
}
print(" done with implicit label")
}
// Return to anonymous function
fun foo() {
listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {
if (value == 3) return // local return to the caller of the anonymous function - the forEach loop
print(value)
})
print(" done with anonymous function")
}
When(更强大的Switch)
fun describe(obj: Any): String =
when (obj) {
1 -> "One"
"Hello" -> "Greeting"
is Long -> "Long"
!is String -> "Not a string"
else -> "Unknown"
}
Ranges
使用in关键字来判断一个变量是否处在这个区间中
val x = 10
val y = 9
if (x in 1..y+1) {
println("fits in range")
}
类型检查和自动转换
fun getStringLength(obj: Any): Int? {
if (obj is String) {
// `obj` is automatically cast to `String` in this branch
return obj.length
}
// `obj` is still of type `Any` outside of the type-checked branch
return null
}
// 或者
fun getStringLength(obj: Any): Int? {
if (obj !is String) return null
// `obj` is automatically cast to `String` in this branch
return obj.length
}
不安全转换
val x: String = y as String
安全转换
val x: String? = y as? String
代理
代理使用关键字by,代表的是将原本应该自己职责代理给其他的类或者属性
类代理
interface Base {
fun printMessage()
fun printMessageLine()
}
class BaseImpl(val x: Int) : Base {
override fun printMessage() { print(x) }
override fun printMessageLine() { println(x) }
}
// 交给代理类Base去做
class Derived(b: Base) : Base by b {
// 也可以重新override
override fun printMessage() { print("abc") }
}
fun main() {
val b = BaseImpl(10)
Derived(b).printMessage()
Derived(b).printMessageLine()
}
属性代理
一个规划化的栗子
class Example {
// set p, get p will delegate by Delegate getValue/setValue
var p: String by Delegate()
}
The syntax is: val/var <property name>: <Type> by <expression>. The expression after by is a delegate, because the get() (and set()) that correspond to the property will be delegated to its getValue() and setValue() methods. Property delegates don’t have to implement an interface, but they have to provide a getValue() function (and setValue() for vars).
import kotlin.reflect.KProperty
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name}' in $thisRef.")
}
}
val e = Example()
println(e.p)
常用的属性代理有lazy和observable
// The first call to get() executes the lambda passed to lazy() and remembers the result.
// Subsequent calls to get() simply return the remembered result.
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
fun main() {
// computed!
// Hello
println(lazyValue)
// Hello
println(lazyValue)
}
// observable
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("<no name>") {
prop, old, new ->
println("$old -> $new")
}
}
fun main() {
val user = User()
// <no name> -> first
user.name = "first"
// first -> second
user.name = "second"
}
运算符重载
运算符重载允许你重新自定义已经定义好的操作,比如”+”, “-”等等。实现运算符重载,需要使用到operator关键字
member方式
interface IndexedContainer {
operator fun get(index: Int)
}
// 重写时可以忽略operator关键字
class OrdersList: IndexedContainer {
override fun get(index: Int) { /*...*/ }
}
函数扩展方式
data class Point(val x: Int, val y: Int)
operator fun Point.unaryMinus() = Point(-x, -y)
val point = Point(10, 20)
fun main() {
println(-point) // prints "Point(x=-10, y=-20)"
}
Scope functions
作用域函数的目的就是让你在一个特定的object context中执行你的函数体,在这个作用域中,你能获取到这个object而无需知道它的名字
具体的作用域函数主要有:let,run,with,apply 和 also
| Function | Object reference | Return value | Is extension function |
|---|---|---|---|
| let | it | Lambda result | Yes |
| run | this | Lambda result | Yes |
| run | - | Lambda result | No: called without the context object |
| with | this | Lambda result | No: takes the context object as an argument. |
| apply | this | Context object | Yes |
| also | it | Context object | Yes |
使用this还是it
在作用域函数中,你有两种方式获取到这个context object. lambda receiver(this) 或者 lambda argument(it)
run,with 和 apply使用this指向这个context object,一般情况下,this可以省略。推荐使用this的情况是,需要访问或者设置这个对象的一些属性或者方法。
val adam = Person("Adam").apply {
age = 20 // same as this.age = 20 or adam.age = 20
city = "London"
}
println(adam)
let,also使用it指向这个object. it适用的场景是,这个object作为变量或者参数使用。
fun getRandomInt(): Int {
return Random.nextInt(100).also {
writeToLog("getRandomInt() generated value $it")
}
}
val i = getRandomInt()
println(i)
返回值
apply和also返回context object(主要适用于进行对象的链式处理)let,run和with返回的是 lambda的执行结果
run
// this, Lambda result
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
run的使用场景是:当这个lambda中同时包含对象初始化以及返回值计算
val service = MultiportService("https://example.kotlinlang.org", 80)
val result = service.run {
port = 8080
query(prepareRequest() + " to port $port")
}
// the same code written with let() function:
val letResult = service.let {
it.port = 8080
it.query(it.prepareRequest() + " to port ${it.port}")
}
另外,run也可以作为非扩展函数使用,作为非扩展函数时,主要用来通过计算一系列语句声明来得到返回表达式。
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
// example
val hexNumberRegex = run {
val digits = "0-9"
val hexDigits = "A-Fa-f"
val sign = "+-"
Regex("[$sign]?[$digits$hexDigits]+")
}
for (match in hexNumberRegex.findAll("+123 -FFFF !%*& 88 XYZ")) {
println(match.value)
}
let
// it, Lambda result
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
用来执行一个或者多个函数链式调用后的结果
val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map { it.length }.filter { it > 3 }.let {
println(it)
// and more function calls if needed
} // 如果一个lambda块中,只有一个函数并且使用it作为它的参数
// 则可以使用函数引用(::)来代替这个lambda
numbers.map { it.length }.filter { it > 3 }.let(::println)
用作判空检查
val str: String? = "Hello"
//processNonNullString(str) // compilation error: str can be null
val length = str?.let {
println("let() called on $it")
processNonNullString(it) // OK: 'it' is not null inside '?.let { }'
it.length
}
提升代码的可阅读性
val numbers = listOf("one", "two", "three", "four")
val modifiedFirstItem = numbers.first().let { firstItem ->
println("The first item of the list is '$firstItem'")
if (firstItem.length >= 5) firstItem else "!" + firstItem + "!"
}.uppercase()
println("First item after modifications: '$modifiedFirstItem'")
apply
// this, Context Object
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
apply常用作对象的配置,例如
val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)
also
// it, Context Object
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
also主要用来执行一些需要将object作为参数的操作
val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")
with
// this, Lambda result
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
持有object引用,执行函数,无需返回
val numbers = mutableListOf("one", "two", "three")
with(numbers) {
println("'with' is called with argument $this")
println("It contains $size elements")
}
object的属性或者方法参与值的计算
val numbers = mutableListOf("one", "two", "three")
val firstAndLast = with(numbers) {
"The first element is ${first()}," +
" the last element is ${last()}"
}
println(firstAndLast)
Kotlin笔记小结(For Java Developer)的更多相关文章
- 学习笔记(三)--->《Java 8编程官方参考教程(第9版).pdf》:第十章到十二章学习笔记
回到顶部 注:本文声明事项. 本博文整理者:刘军 本博文出自于: <Java8 编程官方参考教程>一书 声明:1:转载请标注出处.本文不得作为商业活动.若有违本之,则本人不负法律责任.违法 ...
- 时隔两年最近再次折腾opensuse 的一些笔记 - opensuse linux java service shell
时隔两年最近再次折腾opensuse 的一些笔记 - opensuse linux java service shell opensuse 一些常用命令: service xxx start/s ...
- Kotlin 或将取代 Java——《Java 编程思想》作者 Bruce Eckel [转]
Bruce Eckel 是<Java 编程思想>.<C++编程思想>的作者,同时也是 MindView 公司的总裁,该公司向客户提供软件咨询和培训.他是 C++ 标准委员会拥有 ...
- Groovy小结:java调用Groovy方法并传递参数
Groovy小结:java调用Groovy方法并传递参数 @(JAVA总结) 1. 场景描述 在网上查了资料发现,java有三种方式调用groovy脚本.但是真正在实际的服务器环境中,嵌入groovy ...
- What skills you need to become a full stack java developer?
For a full stack Java developer you should start with learning backend and front-end technologies Fr ...
- Java Developer's Guide to SSL Certificates
https://www.codebyamir.com/blog/java-developers-guide-to-ssl-certificates Overview When developing w ...
- openresty 学习笔记小结:综合应用实例
openresty 学习笔记小结:综合应用实例 这个综合实验实现的功能其实很简单,用户访问一个页面,显示一个默认页面.输入参数(post或者get都可以),如果参数在数据库查询得到并满足一定条件,根据 ...
- Kotlin笔记
官网: http://kotlinlang.org/ http://kotlinlang.org/docs/reference/ 中文教程: http://kotlindoc.com/ Gradle: ...
- Java学习笔记之:Java 流
一.介绍 Java.io包几乎包含了所有操作输入.输出需要的类.所有这些流类代表了输入源和输出目标. Java.io包中的流支持很多种格式,比如:基本类型.对象.本地化字符集等等. 一个流可以理解为一 ...
随机推荐
- NSMutableArray基本概念
1.NSMutableArray介绍 什么是NSMutableArray NSMutableArray是NSArray的子类 NSArray是不可变的,一旦初始化完毕后,它里面的内容就永远是固定的, ...
- tomcat启用apr需要的三个组件(缺少可能会报错)
tomcat8开始支持Apr,可以提升IO性能,但若配置了使用Apr,如下图所示,则需要安装apr和aprutil和tomcat-native 如果采用nio的方式,则配置可以改成protocol=& ...
- 这个Dubbo注册中心扩展,有点意思!
今天想和大家聊聊Dubbo源码中实现的一个注册中心扩展.它很特殊,也帮我解决了一个困扰已久的问题,刚刚在生产中用了,效果很好,迫不及待想分享给大家. Dubbo的扩展性非常灵活,可以无侵入源码加载自定 ...
- Linux常用命令整理:文件目录管理
据说,你要对Linux文件做的事情,98%都记录在这篇文章里了. 1.ls命令 最常见的命令,相信刚进入linux命令行界面的时候,都要用这个命令看看当前目录下都有哪些文件吧. 名称:List 含义: ...
- Hadoop完全分布式的配置
选取机器sam01作为主节点,并进行分布式文件的配置 1.进入Hadoop配置文件路径/usr/local/hadoop/etc/hadoop(这里我把Hadoop安装在/usr/local目录下) ...
- 微服务从代码到k8s部署应有尽有系列(一)
从本篇文章开始,我们用一个系列来讲解从需求到上线.从代码到k8s部署.从日志到监控等各个方面的微服务完整实践. 实战项目地址:https://github.com/Mikaelemmmm/go-zer ...
- 【高频Java面试题】简单说说JVM堆的内存结构和GC回收流程
目录 前言 JVM堆内存结构简述 JVM堆内存结构图 堆初体验 结构详情 新生代 老年代 永久代/元空间 GC回收流程 GC回收流程图 GC回收详细流程 查看JDK自带可视化堆空间图 总结 前言 我们 ...
- 用 JuiceFS 备份 Nginx 日志可以这么简单
在我们线上的生产环境中要备份的东西很多,各种服务日志.数据库数据.用户上传数据.代码等等.用 JuiceFS 来备份可以节省你大量时间,我们会围绕这个主题写一系列的教程,整理出一套最佳实践,方便大家. ...
- MySQL高级优化
MySQL高级 1.索引是什么? (1)索引是排好序可以快速查找的数据结构 (2)方便快速查找,索引实际上也是一张表所以也是要占内存的 2.索引存在哪里? (1)InnoDB引擎 ①索引是和数据存放在 ...
- 设置maven创建工程的jdk编译版本
方式一:在maven的主配置文件中指定创建工程时使用jdk1.8版本 <profile> <id>jdk-1.8</id> <activation> & ...