Kotlin和Java互相調用(二)

Kotlin反射

正如在前面不少代碼中所見到的季惯, Kotlin也提供了反射 API,這些反射 API可以方便程序在運行時自省程序的結構 永票。 Kotlin把函數和屬性當成“ 一等公民”奖慌, 并可通過反射直接獲取函數、屬性的引用西乖。

使用 Kotlin的反射API需要添加單獨的JAR文件(kotlin-reflect.jar)狐榔,這樣可以方使程序在不使用反射時減小運行庫的大小坛增。

類引用

Kotlin的類引用使用 KClass代表,如果要獲取己知的 Kotlin類的 KClass對象薄腻,則可通過如下語法 :

val c = MyClass : :class

上面這種語法在前面程序中已經多次見到收捣。需要說明的是,Kotlin的類引用是 KClass 對象庵楷, Java 的類引用是 java.lang.Class 對象罢艾,它們二者是不同的,如果需要通過KClass 獲取對應的 java.lang.Class 對象尽纽,則可調用 KClass 對象的 java 屬性 咐蚯。
如果己有一個Kotlin對象, 則同樣可通過::class語法來獲取該對象的類引用蜓斧。例如如下語法:

val c = myObj : :class

從KClass獲取類信息

獲取 KClass對象之后仓蛆,即可通過KClass提供的大量方法或屬性來獲取該 KClass對象所對應類的詳細信息。
下面程序示范了通過 KClass 來獲取類的詳細信息挎春。該程序示范了 KClass 所包含的部分屬性和方法

import kotlin.reflect.full.*

//定義注解
annotation class Anno

//使用 3 個注解修飾該類
@Deprecated("該類已經不推薦使用 ")
@Anno
@Suppress(" UNCHECKED CAST")
class ClassTest(age: Int) {
    var name: String = "Kotlin"

    //為該類定義一個私有的構造器
    private constructor() : this(20) {
    }

    //定義一個有參數的構造器
    constructor(name: String) : this(15) {
        println("執(zhí)行有參數的構造器:${name}")
    }

    //定義一個無參數的 info 方法
    fun info() {
        println("執(zhí)行無參數的 info 方法 ")
    }

    //定義一個有參數的 info 方法
    fun info(str: String) {
        println("執(zhí)行有參數的info方法看疙,其str參數值:$str")
    }

    //定義一個測試用的嵌套類
    class Inner
}

//為 ClassTest 定義擴展方法
fun ClassTest.bar() {
    println("擴展的 bar 方法")
}

//為 ClassTest 定義擴展屬性
val ClassTest.foo: Double
    get() = 2.4

fun main(args: Array<String>) {
    //下面代碼可以獲取 ClassTest 對應的 KClass
    val clazz = ClassTest::class
    //通過 constructors 屬性獲取 KClass 對象所對應類的全部構造器
    val ctors = clazz.constructors
    println("ClassTest 的全部構造器如下: ")
    ctors.forEach {
        println(it)
    }
    println("ClassTest 的主構造器如下: ")
    println(clazz.primaryConstructor)

    //通過 functions 屬性獲取該 KClass 對象所對應類的全部方法
    var funs = clazz.functions
    println("ClassTest 的全部方法如下: ")
    funs.forEach {
        println(it)
    }

    //通過 declaredFunctions 屬性獲取該 KClass 對象 本身所聲明的全部方法(不包括繼承的方法)
    val funs2 = clazz.declaredFunctions
    println("ClassTest 本身聲明的全部方法如下: ")
    funs2.forEach {
        println(it)
    }

    //通過 declaredMemberFunctions 屬性獲取該 KClass 對象 本身所聲明的全部成員方法(不包括繼承的方法〉
    val memberFunctions = clazz.declaredMemberFunctions
    println("ClassTest 本身聲明的成員方法如下: ")
    memberFunctions.forEach {
        println(it)
    }

    //通過 memberExtensionFunctions 屬性獲取該 KClass 對象 所代表類的全部擴展方法(不包括繼承的方法)
    val extensionFunctions = clazz.memberExtensionFunctions
    println("ClassTest 本身聲明的擴展方法如下: ")
    extensionFunctions.forEach {
        println(it)
    }

    //通過 declaredMemberProperties 屬性獲取該 KClass 對象 本身所聲明的全部成員屬性(不包括繼承的屬性〉
    val memberProperties = clazz.declaredMemberProperties
    println("ClassTest 本身聲明的成員屬性如下: ")
    memberProperties.forEach {
        println(it)
    }

    //通過 memberExtensionProperties 屬性獲取該 KClass 對象 所代表類的全部擴展屬性(不包括繼承的屬性〉
    val extensionProperties = clazz.memberExtensionProperties
    println("ClassTest 本身聲明的擴展屬性如下: ")
    extensionProperties.forEach {
        println(it)
    }

    //通過 annotations 屬性獲取該 KClass 對象所對應類的全部注解
    val anns = clazz.annotations
    println("ClassTest 的全部注解如下: ")
    anns.forEach {
        println(it)
    }

    println("該 KClass 元素上的@Anno注解為: "+ clazz.findAnnotation<Anno>())

    //通過 nestedClasses 屬性獲取該 KClass 對象所對應類的全部嵌套類(包括內部類和嵌套類)
    val inners = clazz.nestedClasses
    println("ClassTest 的全部內部類如下: ")
    inners.forEach {
        println(it)
    }

    //通過 supertypes 屬性獲取該類的所有父類型(包括父類和父接口)
    println("ClassTest 的父類型為: " + clazz.supertypes)
}

運行結果:

ClassTest 的全部構造器如下:
fun <init>(): test11.ClassTest
fun <init>(kotlin.String): test11.ClassTest
fun <init>(kotlin.Int): test11.ClassTest
ClassTest 的主構造器如下:
fun <init>(kotlin.Int): test11.ClassTest
ClassTest 的全部方法如下:
fun test11.ClassTest.info(): kotlin.Unit
fun test11.ClassTest.info(kotlin.String): kotlin.Unit
fun kotlin.Any.equals(kotlin.Any?): kotlin.Boolean
fun kotlin.Any.hashCode(): kotlin.Int
fun kotlin.Any.toString(): kotlin.String
ClassTest 本身聲明的全部方法如下:
fun test11.ClassTest.info(): kotlin.Unit
fun test11.ClassTest.info(kotlin.String): kotlin.Unit
ClassTest 本身聲明的成員方法如下:
fun test11.ClassTest.info(): kotlin.Unit
fun test11.ClassTest.info(kotlin.String): kotlin.Unit
ClassTest 本身聲明的擴展方法如下:
ClassTest 本身聲明的成員屬性如下:
var test11.ClassTest.name: kotlin.String
ClassTest 本身聲明的擴展屬性如下:
ClassTest 的全部注解如下:
@kotlin.Deprecated(level=WARNING, replaceWith=@kotlin.ReplaceWith(imports={}, expression=""), message="該類已經不推薦使用 ")
@test11.Anno()
該 KClass 元素上的@Anno注解為: @test11.Anno()
ClassTest 的全部內部類如下:
class test11.ClassTest$Inner
ClassTest 的父類型為: [kotlin.Any]

值得指出的是,雖然定義ClassTest類時使用了@Suppress 注解直奋,但程序運行時無法分析出該類中包含的該注解能庆,這是因為@Suppress 使用了@Retention(SOURCE)修飾,這表明 @Suppress 只能保存在源代碼級別上脚线,而通過 ClassTest.class 獲取的是該類的運行時 KClass 對象搁胆,所以程序無法訪問到@ Suppress 注解。

通過 KClass對象可以得到大量的 KFunction邮绿、 KProperty (它們都是 KCallable 的子類)等對象渠旁,這些對象分別代表該類所包括的方法(包括構造器)和屬性等,程序還可以通過這些對象來執(zhí)行實際的功能船逮,例如調用方法顾腊、創(chuàng)建實例等。

創(chuàng)建對象

獲取 KClass 對象之后挖胃,調用該對象的 createlnstance()方法即可創(chuàng)建該類的實例杂靶,該方法總是調用 KClass所代表類的無參數的構造器來創(chuàng)建實例。
如果需要調用有參數的構造器來創(chuàng)建實例酱鸭,則可通過 KClass 的 constructors 屬性來獲取所有構造器吗垮,該屬性返回 Collection<Function>集合對象,這意味著構造器的本質依然是一個函 數凹髓。
獲取 KClass 的所有構造器之后烁登,接下來程序可根據需要調用指定的構造器來創(chuàng)建實例。下面程序示范了如何通過 KClass 創(chuàng)建實例 蔚舀。

import kotlin.reflect.full.*

class Item(var name: String) {
    var price = 0.0

    constructor() : this("未知商品") {
        this.price = 0.0
    }

    constructor(name: String, price: Double) : this(name) {
        this.price = price
    }
}

fun main(args: Array<String>) {
    val clazz = Item::class
    //createinstance ()方法調用無參數的構造器創(chuàng)建實例
    val inst1 = clazz.createInstance()
    //未知商品
    //0.0
    println(inst1.name)
    println(inst1.price)

    //獲取所有構造器
    val cons = clazz.constructors
    cons.forEach {
        if (it.parameters.size == 2) {
            //調用帶兩個參數的構造器創(chuàng)建實例
            val inst2 = it.call("kotlin",78.9)
            //kotlin
            //78.9
            println(inst2.name)
            println(inst2.price)
        }
    }
}

構造器引用

正如前文所介紹的饵沧,構造器的本質是一個函數蚀之,即一個返回值為當前類實例的函數。 因此程序可將構造器引用當成函數使用捷泞。
此外, Kotlin 允許通過使用“::”操作符并添加類名來引用該類的主構造器 寿谴。 例如如下程序锁右。

class Foo(var name: String = "未知")

//test 函數的參數是(String)->Foo 類型(這就是 Foo 帶 String 參數的構造器的類型)
fun test(factory: (String) -> Foo) {
    val x: Foo = factory("瘋狂 Kotlin 講義")
    println(x.name)
}

fun main(args: Array<String>) {
    //通過: :Foo 引用 Foo 類的主構造器
    test(::Foo)
}

上面代碼調用 test()函數時需要傳入一個(String)->Foo 類型的參數,這就是 Foo 類主構造器的類型讶泰,因此將::Foo 作為參數傳入 咏瑟。
在某些時候,如果要獲取 Kotlin構造器引用對應的 Java構造器對象(Constructor)痪署,則可通過調用 KFunction 的擴展屬性 javaConstructor來實現码泞。
例如如下代碼:

::Foo. javaConstructor

需要說明的是,如果要調用構造器引用的 javaConstructor屬性狼犯,則需要導入kotlin.reflect.jvm包余寥,因為這些擴展屬性都屬于與 Java反射互相操作的部分,被定義在 kotlin.reflect.jvm包下悯森。

調用方法

正如前面所見到的宋舷,所有構造器和方法都屬于KFunction的實例,因此它們都可以通過call() 方法來調用瓢姻。 所以祝蝠,程序要調用指定類的方法,只要先獲取方法的KFunction實例幻碱,然后調用call()方法即可 绎狭。
使用 KFunction調用方法時,有一點需要說明: 由于方法是面向對象的概念褥傍,因此它有一 個主調者儡嘶。比如 一句漢語:“豬八戒吃西瓜”,換成面向對象的寫法就是 :

豬八戒 .吃(西瓜)

但如果換成函數式寫法就是 :

吃(豬八戒,西瓜)

對比這兩種寫法可以看出摔桦,面向對象的方法如果帶 N個參數社付,那么轉換成函數式調用時就會變成 N+I 個參數。
如下程序示范了調用指定函數邻耕。

class Foo1 {
    fun test(msg: String) {
        println("執(zhí)行帶 String 參數的 test 方法 : ${msg}")
    }

    fun test(msg: String, price: Double) {
        println("執(zhí)行帶String, Double參數的 test方法:  ${msg}  ${price}")
    }
}

fun main(args: Array<String>) {
    val clazz = Foo1::class
    //創(chuàng)建 Foo 類的實例
    val ins = clazz.createInstance()
    //獲取 clazz 所代表類直接定義的全部函數
    val funs = clazz.declaredFunctions
    for (f in funs) {
        //如果函數具有 3 個參數(對應帶 2 個參數的方法)
        if (f.parameters.size == 3) {
            //調用帶 3 個參數的函數 
            //執(zhí)行帶String, Double參數的 test方法:  Kotlin  78.8
            f.call(ins, "Kotlin", 78.8)
        }

        //如果函數具有 2 個參數(對應帶 1 個參數的方法〉
        if (f.parameters.size == 2) { 
            //調用帶 2 個參數的函數
            //執(zhí)行帶 String 參數的 test 方法 : Kotlin
            f.call(ins, "Kotlin")
        }
    }
}

從上面程序可以看出 鸥咖, 執(zhí)行帶3個參數的函數實際上是調用帶2個參數的方法,執(zhí)行帶2個參數的函數實際上是調用帶1個參數的方法兄世,方法的調用者將作為函數的第一個參數被傳入啼辣。

函數引用

Kotlin 的函數也是一等公民,函數也有其自身的類型御滩。Kotiin 程序可以獲取函數的引用鸥拧,把函數當成參數傳入另一個函數中党远。
Kotiin 也通過“::”符號加函數名的形式來獲取特定函數的引用。當存在多個重載函數時富弦, Kotlin可通過上下文推斷出實際引用的是哪個函數: 如果 Kotiin無法通過上下文準確推斷出引用哪個數沟娱,編譯器就會報錯。例如如下程序腕柜。

//定義兩個重載的函數
fun isSmall(i: Int) = i < 5

fun isSmall(s: String) = s.length < 5

fun main(args: Array<String>) {
    val list = listOf(20, 30, 100, 4, -3, 2, -12)

    //由于 filter ()函數需要(Int) ->Boolean 類型的參數济似,故此處 ::isSmall 引用第一個函數
    val resultList = list.filter(::isSmall)
    println(resultList) //輸出[ 4, - 3, 2, -12]

    val strlist = listOf("Java", "Kotlin", "Swift", "Go", "Erlang")
    //由于 filter () 函數需要(String)->Boolean 類型的參數, 故此處 ::isSmall 引用第二個函數
    val resultStrList = strlist.filter(::isSmall)
    println(resultStrList) //輸出[ Java , Go]
    //無法推斷出 :: isSmall 到底引用哪個函數盏缤,報錯
    //val f = ::isSmall
    //可以推斷出 :: isSmall 到底引用哪個函數砰蠢,正確
    var f: (String) -> Boolean = ::isSmall
    println(f("Lua"))
}

從代碼所看到的 , 當 List 集合是 List<String>實例時唉铜,它的 filter()方法需要的參數是(String)->Boolean類型台舱,因此 Kotlin可以準確地從重載函數中引用到符合要求的函數 。
此外需要說明的是潭流,如果 需要引用類的成員方法或擴展方法竞惋,那么需要進行限定。例如 String::toCharArray才能表明引用 String的 toCharArray()方法幻枉,單純地使用 ::toCharAηay()不行 碰声。
String::toCharArray 函數引用的類型也不是簡單的()->CharArray 類型,而是 String.() -> CharArray 類型 熬甫。

有些時候胰挑,程序需要實現某個功能較強的函數, 如果此時系統己經包含了多個細粒度的函數椿肩,那么可以將這些細粒度的函數組合起來實現功能較強的函數瞻颂。 比如業(yè)務需要程序獲取數的平方根,但該函數要做一些額外處理郑象,如果該數是正數贡这,則直接獲取平方根;如果該數是負數,則獲取該數的絕對值的平方根厂榛。

fun abs(d: Double): Double = if (d < 0) -d else d
fun sqrt(d: Double): Double = java.lang.Math.sqrt(d)

//定義一個 comp ()函數盖矫,該函數用于將兩個函數組合起來
fun comp(fun1: (Double) -> Double, fun2: (Double) -> Double): (Double) -> Double {
    return { x -> fun2(fun1(x)) }
}
fun main(args: Array<String>) {
    println(abs(-3.2))
    //將: :abs和::sqrt組合起來
    val f = comp(::abs, ::sqrt)
    println(f(-25.0))
}

上面代碼用 comp()函數將::abs和::sqrt兩個函數組合在一起,這樣就會得到 一個新的函數 : f击奶, 接下來程序可通過f()函數同時完成兩個函數的功能辈双。

在某些時候,如果要獲取 Kotlin 函數引用對應的 Java 方法對象( Method)柜砾,則可通過調用 KFunction 的擴展屬性 javaMethod 來實現 湃望。例如如下代碼:

:: abs.javaMethod

需要說明的是,如果要調用函數引用的 javaMethod 屬性 ,則需要導入kotlin.reflect.jvm包证芭,因為這些擴展屬性都屬于與Java 反射互相操作的部分瞳浦,被定義在 kotlin.reflect.jvm包下。

訪問屬性值

獲取 KClass 對象之后废士,也可通過 KClass對象來獲取該類所包含的屬性叫潦。 Kotlin為屬性提供了眾多的 API。

  • KProperty: 代表通用的屬性 官硝。 它是 KCallable 的子接口诅挑。
  • KMutableProperty: 代表通用的讀寫屬性。它是 KProperty的子接口泛源。
  • KProperty0: 代表無需調用者的屬性(靜態(tài)屬性)。它是 KProperty的子接口 忿危。
  • KMutableProperty0: 代表無需調用者的讀寫屬性(靜態(tài)讀寫屬性)达箍。它是 KProperty0的子接口 。
  • KProperty1 : 代表需要 1 個調用者的屬性(成員屬性)铺厨。它是 KProperty的子接口缎玫。
  • KMutableProperty1:代表需要 1個調用者的讀寫屬性(成員讀寫屬性)。它是 KProperty1的子接口解滓。
  • KProperty2:代表需要 2 個調用者的屬性(擴展屬性) 赃磨。 它是 KProperty的子接口。
  • KMutableProperty2:代表需要 2個調用者的讀寫屬性(擴展讀寫屬性)洼裤。它是 KProperty2的子接口邻辉。

程序獲取代表屬性的 KProperty對象之后,可調用 get()方法來獲取屬性的值;如果程序要設置屬性的值腮鞍,則需要獲取代表屬性的 KMutableProperty 對象值骇。
如下程序示范了如何通過反射來設置和獲取屬性的值 。

class Item2 {
    var name: String = "kotlin"
    val price: Double = 24.1
}

fun main(args: Array<String>) {
    val clazz = Item2::class
    val ins = clazz.createInstance()
    val props = clazz.declaredMemberProperties
    props.forEach {
        when (it.name) {
            "name" -> {
                @Suppress("UNCHECKED_CAST")
                //將屬性轉換為讀寫屬性
                val mp = it as KMutableProperty1<Item2, Any> //修改屬性值
                mp.set(ins, "Java")
                println(it.get(ins))
            }

            "price " -> {
                //只讀屬性移国,只能通過 get ()方法讀取屬性值
                println(it.get(ins))
            }
        }
    }
}

正如上面代碼所看到的吱瘩,當程序要設置 name 屬性的屬性值時,由于 name 屬性是一個成員讀寫屬性迹缀,因此程序將該屬性轉型為 KMutableProperty1 對象使碾,轉型之后程序可調 用 set()方法來設置該屬性的值,如上面代碼所示:如果程序只需獲取該屬性的值祝懂,則調用 KProperty1 的 get()方法即可票摇,如上面代碼所示。

屬性引用

Kotiin 同樣提供了“::”符號加屬性名的形式來獲取屬性引用嫂易,獲取屬性引用也屬于前面介紹的Kproperty 及其子接口的實例 兄朋。
獲取 Kotiin 只讀屬性的引用之后,程序可調用 get()方法來獲取屬性的值;獲取 Kotiin讀寫屬性的引用之后,程序可調用 set()方法來修改屬性的值颅和,也可調用 get()方法來獲取屬性的值傅事。如下程序示范了通過屬性引用來操作屬性。

class Item3 {
    var name: String = "kotlin"
    var price = 16.1
}

var foo = "foo"

fun main(args: Array<String>) {
    //獲取 foo 屬性峡扩,屬于 KMutablePropertyO 的實例
    val topProp = ::foo
    topProp.set("修改后的屬性")
    // println(topProp.get())
    println(foo)

    val im = Item3()
    //獲取 Item 的 name 屬性蹭越,屬于 KMutablePropertyl1的實例
    var namePro = Item3::name
    namePro.set(im, "xq")
    println(namePro.get(im))

    //獲取 Item 的 price 屬性,屬于 KProperty1 的實例
    val prop = Item3::price
    println(prop.get(im))
}

上面代碼獲取頂級讀寫屬性(靜態(tài)讀寫屬性)教届,程序直接用“::”加 屬性名的形式即可响鹃。該屬性引用是 KMutableProperty0 的實例 , 因此該屬性既可通過 set()方法 改變屬性的值案训,也可通過 get()方法獲取屬性的值买置。
第代碼獲取 Item類的name讀寫屬性,程序需要用類名::屬性名的形式來獲取指定類的讀寫屬性强霎。該屬性是 KMutableProperty1 的實例忿项,因此程序也可通過 set()、 get()方法來修改城舞、獲取屬性的值 轩触。

與前面介紹的構造器、函數相似家夺, Kotiin 在 kotlin.reflect.jvm 包下也提供了Kotlin 屬性與Java 反射互操作的擴展屬性 脱柱。 由于 Kotlin 屬性會對應于 Java 的 3 種成員,因此 KProperty 包含如下 3 個擴展屬性 拉馋。

  • javaField : 獲取該屬性的幕后字段(如果該屬性有幕后字段的話)榨为。 該屬性返回java.lang.reflect.Field對象。
  • javaGetter : 獲取該屬性的 getter方法 煌茴。 該屬性返回 java.lang.reflect.Method 對象 柠逞。
  • javaSetter:獲取該屬性的 setter 方法(如果該屬性是讀寫屬性的話) 。 該屬性返回
    java.lang.reflect. Method對象景馁。

一旦獲取了 Kotlin屬性的幕后字段的 Field對象板壮、getter和 setter方法的Method對象之后,剩下的就是 Java反射的事了合住。如下程序示范了通過 Kotiin屬性來獲取 Java反射 API绰精。

class Item3 {
    var name: String = "kotlin"
    var price = 16.1
}

var foo = "foo"

fun main(args: Array<String>) {
    //獲取 foo 屬性,屬于 KMutablePropertyO 的實例
    val topProp = ::foo
    println(topProp.javaField)
    //獲取幕后字段
    println(topProp.javaGetter)
    //獲取 getter方法
    println(topProp.javaSetter)


    //獲取 Item3 的 name 屬性透葛,屬于 KMutableProperty1 的實例
    val mp = Item3::name
    //獲取幕后字段
    println(mp.javaField)
    //獲取 getter 方法
    println(mp.javaGetter)
    //獲取 setter方法
    println(mp.javaSetter)


    //獲取 Item3 的 price 屬性 ,屬于 KMutableProperty1 的實例
    val prop = Item3::price
    //獲取幕后字段
    println(prop.javaField)
    //獲取 getter 方法
    println(prop.javaGetter)
}

綁定的方法與屬性引用

前面介紹的都是通過 KClass (類本身)來獲取方法或屬性的引用的笨使,當函數或屬性不在任何類中定義時,程序直接使用“::”加函數名(或屬性名)的形式來獲取函數或屬性的引用僚害, 這些函數或屬性都沒有綁定任何對象硫椰,因此調用函數或屬性時第一個參數必須傳入調用者。

從 Kotlin 1.1 開始, Kotlin 支持一種“綁定的方法或屬性引用”靶草,這種方法或屬性引用不是通過類獲取的蹄胰,而是通過對象獲取的,這意味著該方法或屬性己經綁定了調用者奕翔,因此程序執(zhí)行這種方法或屬性時無須傳入調用者裕寨。
如下程序示范了綁定的方法或屬性引用。

fun main(args: Array<String>) {
    val str = "Kotlin"
    //獲取對象綁定的方法
    val f: (CharSequence, Boolean) -> Boolean = str::endsWith
    //調用綁定的方法時無須傳入調用者
    println(f("lin", true))
    //獲取對象綁定的屬性
    val prop = str::length
    //調用綁定的屬性時無須傳入調用者
    println(prop.get())

    var list = listOf("Kotlin", "Java", "Go", "Erlang")
    //獲取對象綁定的方法
    val fn = list::subList
    //調用綁定的方法時無須傳入調用者
    println(fn(1, 3)) //輸出["Java","Go"]
    // 獲取對象綁定的屬性
    val prp = list::indices
    //調用綁定的屬性時無須傳入調用者
    println(prp .get()) //輸出 0 .. 3
}

上面程序先定義了 一個簡單的字符串派继,代碼使用對象來獲取綁定的方法宾袜, 這樣當程序調用方法時只要傳入該方法的兩個參數即可,無須傳入調用者;后面代碼使用對象來獲取綁定的屬性驾窟,這樣程序直接使用 get()方法即可獲取綁定的屬性值庆猫,無須傳入調用者。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末绅络,一起剝皮案震驚了整個濱河市阅悍,隨后出現的幾起案子,更是在濱河造成了極大的恐慌昨稼,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拳锚,死亡現場離奇詭異假栓,居然都是意外死亡,警方通過查閱死者的電腦和手機霍掺,發(fā)現死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門匾荆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人杆烁,你說我怎么就攤上這事牙丽。” “怎么了兔魂?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵烤芦,是天一觀的道長。 經常有香客問我析校,道長构罗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任智玻,我火速辦了婚禮遂唧,結果婚禮上,老公的妹妹穿的比我還像新娘吊奢。我一直安慰自己盖彭,他們只是感情好,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著召边,像睡著了一般铺呵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上掌实,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天陪蜻,我揣著相機與錄音,去河邊找鬼贱鼻。 笑死宴卖,一個胖子當著我的面吹牛,可吹牛的內容都是我干的邻悬。 我是一名探鬼主播症昏,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼父丰!你這毒婦竟也來了肝谭?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤蛾扇,失蹤者是張志新(化名)和其女友劉穎攘烛,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體镀首,經...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡坟漱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了更哄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芋齿。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖成翩,靈堂內的尸體忽然破棺而出注竿,到底是詐尸還是另有隱情览爵,我是刑警寧澤挨摸,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布帘不,位于F島的核電站,受9級特大地震影響术羔,放射性物質發(fā)生泄漏职辅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一聂示、第九天 我趴在偏房一處隱蔽的房頂上張望域携。 院中可真熱鬧,春花似錦鱼喉、人聲如沸秀鞭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锋边。三九已至皱坛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間豆巨,已是汗流浹背剩辟。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留往扔,地道東北人贩猎。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像萍膛,于是被迫代替她去往敵國和親吭服。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內容

  • Kotlin 調用 Java 由于 Kotlin本身并沒有提供強大的類庫支持蝗罗,Kotlin只是一種語言艇棕,因此 Ko...
    凌寒天下獨自舞閱讀 14,028評論 0 10
  • 前言 人生苦多,快來 Kotlin 串塑,快速學習Kotlin沼琉! 什么是Kotlin? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,146評論 9 118
  • 第3章 基本概念 3.1 語法 3.2 關鍵字和保留字 3.3 變量 3.4 數據類型 5種簡單數據類型:Unde...
    RickCole閱讀 5,099評論 0 21
  • 腳下的步子是急促的 豆大的雨點打在傘上又濺到地上 遠處是朦朧的紅燈 是誰站在十字路口 也許下一秒 我將和你相遇在冷...
    菠菜葉子閱讀 146評論 0 0