最近在學習kotlin,隨便總結一下知識點。有興趣的同學可以好好看看哦,每一段都有代碼示例!!!
一、基礎知識
1、聲明變量
fun main(){
//變量
var a:Int = 1;
//常量
val b:Int = 2;
}
這裏a、b表示變量的名字,冒號後面表示變量的類型。用val表示常量。
fun main(){
//不可爲空的類型
var str1:String = "aaa";
//可以爲空的類型
var str2:String? = null;
str1 = str2
str2 = str1
}
用?表示這個變量可以爲null,如果不加表示不可爲空,編譯會報錯。str1 = str2這個是無法通過編譯的。
2、函數相關
fun printLen(str:String):String{
printLn("$str")
return str;
}
Kotlin中的函數可以不放在類裏面,單獨提出來。實際上編譯後的字節碼文件依然是放在一個類裏面。printLn("$str")這個$符號,表示這個變量可以放在字符串裏,我們就減少了字符串的拼接。
3、與java中的不同
(1)內部類
class Main{
object Test {
fun sayMessage(msg: String) {
println(msg)
}
}
fun main(){
println(Test.sayMessage("aaa"))
}
}
Test是一個匿名內部類,在java中,匿名內部類會被單獨編譯成一個class文件,在外部類的內部有一個單例的對象。
java:
Test.INSTANCE.sayMessage("aaa")
kotlin:
Test.sayMessage("aaa")
這其實也是kotlin創造單例對象的方法。
(2)字節碼對象
class Main{
object Test {
fun sayMessage(msg: String) {
println(msg)
}
}
fun main(){
getName(JavaBean::class.java)
getName(KotlinBean::class)
}
fun getName(clazz:Class<JavaBean>){
println(clazz.simpleName)
}
fun getName(clazz:KClass<KotlinBean>){
println(clazz.simpleName)
}
}
public class JavaBean {
}
class KotlinBean
在kotlin中獲得java的字節碼xx::class.java,對於kotlin本身的對象類型是KClass傳入的是xx::class。
(3)解決關鍵字衝突
public class JavaBean {
public static int in = 1;
}
class Main{
fun main(){
println(JavaBean.`in`)
}
}
in 在java中聲明爲了變量,但是在Kotlin中確是關鍵字,所以呢,要用``來處理。同時``可以將不合法字符變成合法的,比如:fun `123`{}。
4、kotlin與java互調產生的問題
kotlin 沒有封裝類,調用java方法,只會執行基本數據類型方法
public interface TestInterface {
void putNum(int num);
void putNum(Integer num);
}
public class Impl implements TestInterface{
public static TestInterface test = new Impl();
@Override
public void putNum(int num) {
System.out.println("int");
}
@Override
public void putNum(Integer num) {
System.out.println("Integer");
}
}
fun main() {
Impl.test.putNum(123);
}
打印輸出的是int。這就是由於kotlin中沒有Integer,所以把兩個方法併爲一個方法了,如果kotlin中實現接口,那麼只需要重寫一個方法就可以了。
kotlin 類型空值敏感
當kotlin調用java方法的時候,課程會返回一個null,如果我們不處理程序會有安全問題。
public class Temp {
public static String format(String str){
return str.isEmpty() ? null : str;
}
}
fun main() {
function("")
}
fun function(str:String){
val fmt1 = Temp.format(str);
println(fmt1)
val fmt2:String = Temp.format(str);
val fmt3:String? = Temp.format(str);
println(fmt3?.length)
}
fmt2會直接在運行的時候報錯,因爲format方法返回的就是null值,fmt1是null。fmt3纔是安全的所以要用?設置成可控類型的,這樣纔是安全的。
kotlin 中沒有靜態變量和靜態方法
在java中調用kotlin方法
class StaticDemo{
object Test{
fun sayMessage(msg:String){
println(msg)
}
}
}
public class Main1 {
public static void main(String[] args) {
StaticDemo.Test.INSTANCE.sayMessage("aaa");
}
}
那我們能不能在java中像調用靜態方法一樣方便呢?就是把INSTANCE去掉?答案是可以的
class StaticDemo{
object Test{
@JvmStatic
fun sayMessage(msg:String){
println(msg)
}
}
}
public class Main1 {
public static void main(String[] args) {
StaticDemo.Test.sayMessage("aaa");
}
}
5、判斷相等
kotlin java
a == b a.equals(b)
a === b a == b
二、函數和Lambda閉包
1、函數的特性語法
//標準寫法
fun echo(name:String){
println("name:$name")
}
//攜帶默認參數
fun echo(name:String = "xiaoming"){
println("name:$name")
}
//簡易寫法,函數中只有一個語句可以搞
fun echo(name:String) = println("name:$name")
默認參數,在java中調用也需要傳參數,在kotlin中不用
2、嵌套函數
fun main() {
function()
}
fun function(){
var str ="hello world"
fun say(count:Int = 10){
println(str)
if(count > 0){
say(count - 1)
}
}
say()
}
用途:(1)在某些條件下,觸發遞歸函數(2)不希望被外部函數訪問的函數
!!!不推薦使用
3、擴展函數
class Main{
}
fun Main.aaa() = print("aaa")
fun main() {
Main().aaa();
}
對一個類的方法進行擴展,類名+ . +方法名。在編譯的時候會編譯到類裏面。
擴展函數是靜態的,不具備運行時的多態!
一般應用於第三方的sdk或者系統類。
4、Lambda閉包語法
lambda:
//java8中
public static void main(String[] args) {
Thread thread = new Thread(() -> System.out.println("aaa"));
}
//kotlin中
fun main() {
var thread = Thread({ -> println("aaaa")})
thread.start()
}
如果lambda是沒有參數的可以更加簡化一點:
var thread1 = Thread({ })
var thread2 = Thread(){}
var thread3 = Thread{}
閉包語法:
//lambda閉包
var print = {
name:String -> println("name:$name")
}
fun main() {
print("aaa")
}
最多傳入22個參數
5、高階函數
將函數作爲參數傳遞給另一個函數...。
fun onlyIf(isDebug:Boolean,block:() -> Unit){
if(isDebug) block();
}
fun main() {
onlyIf(true){
println("打印日誌")
}
}
fun onlyIf(isDebug:Boolean,block:() -> Unit){
if(isDebug) block();
}
fun main() {
val runnable:Runnable = Runnable {
print("runnable::run")
}
var funn:() -> Unit
funn = runnable::run
onlyIf(true,funn)
}
這裏面首先創建了一個對象Runnable。然後有一個lambda函數聲明,利用::將對象中方法拿出來,賦值給funn,然後再調用。
6、內聯函數
inline fun onlyIf(isDebug:Boolean,block:() -> Unit){
if(isDebug) block();
}
fun main() {
val runnable:Runnable = Runnable {
print("runnable::run")
}
var funn:() -> Unit
funn = runnable::run
onlyIf(true,funn)
}
kotlin的Lambda是一個匿名對象。可以使用inline修飾方法,在編譯的時候會拆解方法的調用爲語句調用,進而減少創建不必要的對象。
三、類和對象
1、構造函數
open class Father{
}
class Son:Father(){
}
kotlin類中默認有public final修飾符,默認不允許繼承,如果想要繼承,必須要用open修飾。繼承用:,可以繼承一個父類或者多個接口。
open class Father(var num:Int){
init {
print("father")
}
open fun printClass():Unit{
println("printClass")
}
}
open interface FatherInterface{
fun printInterface()
}
class Son(var a:Int):Father(a),FatherInterface{
override fun printInterface() {
}
override fun printClass(){
}
}
這裏面類名後面的()就是構造函數。如果想要在構造函數中執行某些邏輯就寫在init方法。override表示重寫方法。
多級構造函數
class Test(str:String){
var str:String = str;
var num:Int = 0;
var doub:Double = 0.0;
constructor(str:String,num:Int):this(str){
this.num = num;
}
constructor(str:String,num:Int,doub:Double):this(str,num){
this.doub = doub;
}
}
利用constructor聲明多級構造函數,this()表示調用主構造函數。次級構造函數必須調用主構造函數。
2、訪問修飾符
//前三個跟java的一樣
private
protected
public
//這個指在模塊內能夠訪問,例如module
internal
3、伴生對象
class StringUtils{
companion object {
fun isEmpty(str:String):Boolean{
return "" == str
}
}
}
fun main() {
println(StringUtils.isEmpty(""))
println(StringUtils.isEmpty("aa"))
}
有點像java靜態方法,跟我們上面說的@JvmStatic註解有點像,在編譯的時候爲我們創建了一個靜態內部對象。
4、單例類
class Single constructor(){
companion object {
fun get():Single{
return Holder.instance
}
}
private object Holder{
var instance = Single();
}
}
5、類的動態代理
interface Animal{
fun bark();
}
class Dog:Animal{
override fun bark() {
println("wang")
}
}
class Zoo(animal: Animal):Animal by animal
fun main() {
Zoo(Dog()).bark()
}
輸出wang,如果重寫了bark方法,就不會代理了。kotlin中編譯的時候用的是靜態代理,要比java中的效率高。
6、kotlin特有的類
數據類:
data class User(var id:Long,var name:String)
自動提供toString、equal、hashcode、getter、setter方法。並且類是final。
枚舉類:
enum class Command{
A,B,C,D
}
//下面相當於switch去匹配
fun exec(command: Command) = when (command){
Command.A -> {}
Command.B -> {}
Command.C -> {}
Command.D -> {}
}
超級枚舉:密閉類:
sealed class SuperCommand{
object A:SuperCommand()
object B:SuperCommand()
object C:SuperCommand()
object D:SuperCommand()
}
fun exec(command: SuperCommand) = when (command){
SuperCommand.A -> {}
SuperCommand.B -> {}
SuperCommand.C -> {}
SuperCommand.D -> {}
}