Kotlin basics (vs Java)
Java vs Kotlin
Kotlin is not just Java - only Kotlin/JVM is mentioned in this blog post.
But keep in mind, there’s also Kotlin/Native, Kotlin/Wasm (webassembly) and Kotlin/JS
main function
Java: public static void main
1 |
|
Java in JDK 21 preview feature: 简化了main函数写法, 可以省去explicitly声明class的步骤, 可以省去public
和static
1 |
|
Kotlin
1 |
|
Class constructor
Kotlin: 自动生成all-args constructor和getter
1 |
|
Equivalent to Java
1 |
|
Kotlin: 如果是var
而非val
, 则会自动生成getter和setter
1 |
|
Equivalent to Java
1 |
|
Data class
Data class除了拥有上述的自动生成constructor, getter, setter的功能外,还能生成toString()
, hashcode
, equals
, componentN
, copy
方法
Kotlin
1 |
|
以上kotlin代码完全等同于以下的java代码
- 自动生成构造函数, 且参数包含
@NotNull
annotation和final
关键字, 代表常量 - 自动生成getter和setter. 由于以上使用
val
, 所以只有getter没有setter- 使用kotlin则可以直接通过
p.name
访问, kotlin会自动转换成getter或setter的调用 - 使用java则必须使用
p.getName()
才能访问
- 使用kotlin则可以直接通过
- 自动生成
toString()
,hashcode
,equals
- 如果不想使用kotlin自动生成的toString, 可以手动
override fun toString(): String
代替
- 如果不想使用kotlin自动生成的toString, 可以手动
- 自动生成
componentN()
方法用于解构赋值
Java
1 |
|
如果使用Java+Lombok也有类似的功能
Java + Lombok
1 |
|
Kotlin
如果类的成员带上@JvmField
则不能自己定义getter和setter, 且不能设置为private
1 |
|
支持private
1 |
|
支持默认值
1 |
|
Don’t forget Kotlin also supports 1 or more init
codeblock and 1 or more secondary constructor
1 |
|
Methods
Java
1 |
|
Kotlin
Lambda函数不要写 return
关键词! 函数体内最后一个表达式的值即为返回值
- 函数是Kotlin的一等公民, 函数能够当作普通变量存储, 或作为参数传递
1 |
|
Lambda函数, 如果只有一个入参, 则可以用
it
代表之1
2
3val lambda: (Int) -> Boolean = { num -> num % 2 == 0 }
// can be simplified to:
val lambda: (Int) -> Boolean = { it % 2 == 0 }Lambda函数本身是最后一个参数时, 可以移除括号
1
2
3listOf(1,2,3).filter({it % 2 == 0})
// can be simplified to:
listOf(1,2,3).filter{it % 2 == 0}
kotlin Lambda, 函数, 匿名函数, 将函数作为参数
Kotlin中可以把函数作为参数传递
1 |
|
Java (Java8之前) 中不能把函数作为参数传递. 如果想要实现同样的功能, 只能将函数包进对象的invoke
方法中, 再来传递这个对象.
1 |
|
Kotlin底层使用类似的原理, 实际上kotlin只是提供了“语法糖”, 当你在kotlin中使用函数作为参数传递时, 传递的其实是一个带有invoke
方法的对象.
Kotlin的底层使用类似的原理, 仍然只有对象才能作为args传递. 如果想将函数作为arg传递, 需要使用双冒号 ::
代表 function reference, 代表把一个函数封起来变成一个对象
1 |
|
只有对象才能赋值给一个变量. 函数仍然不可以赋值给变量
1 |
|
将multiply
的function reference对象 ::multiply
赋值给变量 a
后, 就可以使用 a(...args)
调用这个函数了. 虽然看起来这个function reference可以直接使用()
调用, 但jvm实际上执行的仍是对象的 invoke
方法.
1 |
|
函数也可以作为匿名函数传递. Kotlin中匿名函数不是函数, 是对象, 所以才可以传递
1 |
|
大多数情况下, 匿名函数还能再简化成lambda表达式的形式.
再次注意: Lambda函数不要写 return
关键词! 函数体内最后一个表达式的值即为返回值.
1 |
|
如果lambda是函数的最后一个参数, 那么可以把lambda写在函数的外面. (事实上上面的代码会有同样的IDE提示: Lambda argument should be moved out of parentheses)
1 |
|
实际上, lambda函数的参数类型和返回值类型都可以省略.
1 |
|
为什么可以不写? 因为上文operate()
函数的定义已经有明确说明: 需要传入函数的参数和返回值类型为 (Int, Int) -> Int)
1 |
|
NullSatety
Kotlin has null-safety from language level.
1 |
|
Kotlin has “?” chained safety
Kotlin
1 |
|
Equivalent to the following Java code
1 |
|
Kotlin
1 |
|
Equivalent to the following Java code
1 |
|
Kotlin
1 |
|
Equivalent to the following Java code
1 |
|
Kotlin Not-null assertion: will throw NullPointerException at runtime if the value is null
1 |
|
Equivalent to the following Java code
1 |
|
Kotlin safe cast with as?
: Try to cast into the given type, or return null on failure.
1 |
|
Equivalent to the following Java code
1 |
|
Extension
Kotlin允许在不继承类的情况下, 为类扩展方法
例如: 在不拓展String
类的情况下, 为其扩展bold()
方法
Kotlin
1 |
|
Inheritance
Kotlin默认所有class都继承自Any
. Any
类含有equals()
, hashCode()
, toString()
三个方法
Kotlin默认所有class都为final, 无法被继承. 如果想要class可以被继承, 则需添加open
关键字
1 |
|
Equals
Kotlin中 a == b
相当于java中的 a?.equals(b)?:(b===null)
- 如果
a
不为空则等同于a.equals(b)
, 如果a
为空则比较a和b是否同时为空
Kotlin中 a === b
用于比较两个对象是否是同一个instance (类似Python中的 is
). 对于primitive types (例如 Int
则相当于 ==
)
Unit, Nothing, void, java.lang.Void
如果看到一个kotlin函数的返回值是Unit
, 那么它代表 “no meaningful return value”, 大致相当于void
, 但不等于 void
Java中,
void
不是类型 (代表它不是class, 不能赋值给任何变量…), 仅仅代表函数没有返回值Kotlin中,
Unit
是一个类型, 也是一个singleton object (单例对象). 不写函数返回值时, kotlin编译器会隐式加上return Unit.INSTANCE
来返回一个Unit
类型的对象1
2
3
4
5
6
7
8
9fun sayHello() {
println("Hello world")
}
// will be treated as
fun sayHello(): Unit {
println("Hello world")
return Unit.INSTANCE
}Kt和Java可以互相转化: Java中调用kotlin返回值为
Unit
的函数, 则会看到其返回值为void
. Kotlin中调用java里void
返回值的函数, 则会看到其返回Unit
.
Void
是java中的一个class java.lang.Void
. 注意V是大写的.
- 在java中,
Void
是一个class, 但是它的constructor为private, 意味着它 uninstantiable, 你无法手动创建Void
的实例, 除了null
以外无法造出任何Void
的值 - Kotlin中没有
Void
, Kotlin的Void
即为java.lang.Void
.
Nothing
Kotlin中有一个
class Nothing
, 跟java.lang.Void
有些许类似: 它也拥有一个private的constructor, 意味着不可能创建出任何Nothing
的实例. 但是null
也不行 (注意Nothing
和Nothing?
的区别).它的源码如下, 就一行.
1
public class Nothing private constructor()
Nothing有什么作用?
- 当
Nothing
作为函数返回值时, 函数体内无法写任何return
语句 (因为不可能创建出Nothing
类的对象). 常用来给开发者/函数调用者一个提示, 代表这个函数只用来抛异常或这个函数是无限循环, 永远不会返回.
- 当
Public, Protected, Private, Internal
Kotlin中对于top-level
不写则默认
public
标注
private
则仅当前文件可见标注
internal
则同一个module下可见不支持
protected
e.g.
1
2
3
4
5
6
7
8
9
10// file name: example.kt
package foo
fun foo() { ... } // visible everywhere
private fun foo() { ... } // visible only inside example.kt
public var bar: Int = 5 // property is visible everywhere
private set // setter is visible only in example.kt
internal val baz = 6 // visible only inside the same module
对于class的成员
不写则默认
public
protected
,private
与java中相同internal
代表同一个module下可见e.g.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22open class Outer {
private val a = 1 // visible for only this class
protected open val b = 2 // visible for subclasses
internal open val c = 3 // visible for same module
val d = 4 // public by default
protected class Nested { // visible for subclasses
public val e: Int = 5 // If "Nested" is visible, then this is visible
}
}
class Subclass : Outer() {
// a is not visible
// b, c, d, Nested, e are visible
override val b = 5 // 'b' is protected
override val c = 7 // 'c' is internal
}
class Unrelated(o: Outer) {
// o.a, o.b, o.Nested, o.Nested.e are not visible
// o.c and o.d are visible (same module)
}
泛型 <>
Java中不能将子类泛型对象赋值给父类的泛型类型声明. 例如以下代码是不允许的.
1 |
|
这是因为编译器会把不同类型的泛型当作互不相关的类型来处理, List<Parent>
和 List<Child>
是完全不相关的东西. 为了类型安全起见, 不能允许这种操作.
如果允许这种操作, 那么就可以往一个List<Parent>
中加入一个new Parent()
父类对象, 而由于底层的存储实际上是ArrayList<Child>
, 往Child
集合中加入非Child
的对象就会导致运行时ClassCastException
.
解决方法是
Covariant: Producer extends.
任何
List<Parent>
,List<Child>
都能赋值给List<? extends Parent>
1
List<? extends Parent> list = new ArrayList<Child>(); // ✅ ok
副作用是: 只能读取 (从中读出值并当作父类使用), 不能添加 (除了例外null以外, 不能往里添加任何对象)
1
2
3Parent p = list.get(0); // ✅ ok. Can Read value.
list.add(new Child()); // ❌ Compile Error.
list.add(null); // ✅ ok. null is the only value you can add.Contravariant: Consumer Super.
父类泛型类型对象赋值给子类的泛型类型声明
任何
List<Child>
,List<Parent>
,List<Object>
都可以赋给List<? super Child>
。1
List<? super Child> list = new ArrayList<Parent>(); // ✅ ok
副作用是: 只能添加
Child
或Child
的子类, 不能读取 (只能当作Object类读取出来, 因为编译器只能确定list中实际存在的对象是Child
或其父类)1
2
3
4list.add(new Child()); // ✅ ok. You can add Child.
list.add(new SubChild()); // ✅ ok. You can also add its subclasses.
Child c = list.get(0); // ❌ Compile error. Return value is always Object.
Object o = list.get(0); // ✅ You can only treat it as Object.
Kotlin 中 in
和 out
就分别相当于 ? super
和 ? extends
.
1 |
|