Kotlin Koans學(xué)習(xí)筆記(1)

Kotlin Koans是Kotlin官方推出的一系列Kotlin語法練習(xí)。一共42個(gè)任務(wù)喘鸟,分為6個(gè)模塊橡卤。每一個(gè)任務(wù)都有一系列單元測試,需要完成的任務(wù)就是編碼通過單元測試榜跌。本文是在學(xué)習(xí)Kotlin Koans過程中將相關(guān)語法點(diǎn)做一個(gè)簡單的記錄闪唆。

寫在前面,不少童鞋在實(shí)際使用中出現(xiàn)了如下錯(cuò)誤:

Process finished with exit code 1
Class not found: "i_introduction._0_Hello_World._00_Start"Empty test suite.

我本人也復(fù)現(xiàn)了這一個(gè)錯(cuò)誤钓葫,最終按照kotlin-koans 官方的文檔重新導(dǎo)入就可以:

How to build and run tests:

  • 1悄蕾、Working with the project using Intellij IDEA or Android Studio:
    Import the project as Gradle project.
  • 2、To build the project and run tests use test
    task on Gradle panel.

怎么導(dǎo)入為 gralde 工程參考下圖


選擇第4個(gè)菜單導(dǎo)入

有網(wǎng)友問關(guān)于單元測試操作的問題础浮,我使用的是 Android Studio帆调。單元測試的操作我貼兩張截圖說一下吧


運(yùn)行單元測試方法1

運(yùn)行單元測試方法2

0.HelloWorld

和所有其他語言一樣,Kotlin Koans的第一個(gè)任務(wù)名稱就是Hello World豆同,這個(gè)任務(wù)比較簡單番刊,提示也說的很清楚,就是要求task0函數(shù)返回一個(gè)字符串OK

fun task0(): String {
    return "OK"
}

這一個(gè)任務(wù)主要涉及kotlin的函數(shù)定義影锈。在kotlin中函數(shù)通過關(guān)鍵字fun聲明芹务,和Java中函數(shù)的返回類型寫在函數(shù)名稱前不一樣,Kotlin中函數(shù)的返回類型在函數(shù)名稱的后面鸭廷,中間以:分開枣抱。Kotlin中的函數(shù)總是返回一個(gè)值,如果不指定返回值的類型辆床,默認(rèn)返回Uint(類似Java中的Void)佳晶。如果函數(shù)體就是一個(gè)簡單的語句,可以去掉大括弧讼载,用等號(hào)表示:

fun task0(): String = "OK"

1.Java to Kotlin Convert

這個(gè)任務(wù)的要求就是將一段Java代碼轉(zhuǎn)換成Kotlin代碼轿秧,提示可以直接將Java代碼復(fù)制粘貼中跌,然后使用Intellij提供的Convert Java File to Kotlin File功能(僅僅是這個(gè)任務(wù)允許這樣做),非常便捷淤刃。

//Java
public String task1(Collection<Integer> collection) {
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        Iterator<Integer> iterator = collection.iterator();
        while (iterator.hasNext()) {
            Integer element = iterator.next();
            sb.append(element);
            if (iterator.hasNext()) {
                sb.append(", ");
            }
        }
        sb.append("}");
        return sb.toString();
    }
    
//Kotlin

fun todoTask1(collection: Collection<Int>): String
        {
            val sb = StringBuilder()
            sb.append("{")
            val iterator = collection.iterator()
            while (iterator.hasNext()) {
                val element = iterator.next()
                sb.append(element)
                if (iterator.hasNext()) {
                    sb.append(", ")
                }
            }
            sb.append("}")
            return sb.toString()
        }

這一段代碼兩者之間沒有明顯的差別晒他,但是在下一個(gè)任務(wù)中可以看到Kotlin中這一段代碼可以精簡成一行代碼。

2.Named Arguments (命名參數(shù))

任務(wù)的要求是使用Kotlin提供的方法joinToString()重新完成任務(wù)1,只指定joinToString的參數(shù)中的兩個(gè)參數(shù)逸贾。

這里涉及到Kotlin函數(shù)的默認(rèn)參數(shù)(Default Arguments)和命名參數(shù)(Named Arguments)兩個(gè)語法陨仅。

Kotlin中函數(shù)參數(shù)可以有默認(rèn)值,當(dāng)函數(shù)被調(diào)用時(shí)铝侵,如果沒有傳遞對(duì)應(yīng)的參數(shù)灼伤,那么就使用默認(rèn)值。和其他語言相比咪鲜,這以功能可以大大的減少重載函數(shù)的數(shù)目狐赡。參數(shù)的默認(rèn)值在參數(shù)的類型后面通過=賦值。重寫函數(shù)(overriding method)使用和被重寫函數(shù)相同的默認(rèn)參數(shù)疟丙。也就是說當(dāng)我們重寫一個(gè)有默認(rèn)參數(shù)的函數(shù)時(shí)颖侄,我們不允許重新指定默認(rèn)參數(shù)的值

當(dāng)我們?cè)谡{(diào)用函數(shù)時(shí)享郊,可以為傳遞的參數(shù)命名览祖,這在當(dāng)一個(gè)函數(shù)的參數(shù)很多或者函數(shù)參數(shù)具有默認(rèn)值的時(shí)候非常方便。

讓我們回到任務(wù)本身炊琉,該任務(wù)要求使用joinToString函數(shù)重新完成任務(wù)1展蒂,并且只能指定兩個(gè)參數(shù)。

我們來看一下joinToString函數(shù)的定義:

/**
 * Creates a string from all the elements separated using [separator] and using the given [prefix] and [postfix] if supplied.
 * 
 * If the collection could be huge, you can specify a non-negative value of [limit], in which case only the first [limit]
 * elements will be appended, followed by the [truncated] string (which defaults to "...").
 */
public fun <T> Iterable<T>.joinToString(separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): String {
    return joinTo(StringBuilder(), separator, prefix, postfix, limit, truncated, transform).toString()
}

該函數(shù)對(duì)分隔符苔咪,前綴锰悼,后綴等其他參數(shù)都指定了默認(rèn)值,我們?cè)賲⒖既蝿?wù)1中的描述团赏,我們只需要重新指定前綴箕般、后綴兩個(gè)參數(shù)。命名參數(shù)通過在參數(shù)值的前面指定參數(shù)名稱就可以舔清,中間需要一個(gè)=

fun task2(collection: Collection<Int>): String {
    return collection.joinToString(prefix = "{", postfix = "}")
}

3.Default Arguments(默認(rèn)參數(shù))

默認(rèn)參數(shù)的語法在前面已經(jīng)做了介紹隘世,直接來看任務(wù)。任務(wù)要求是將JavaCode3中所有的函數(shù)重載用一個(gè)函數(shù)替換鸠踪。

public class JavaCode3 extends JavaCode {
    private int defaultNumber = 42;

    public String foo(String name, int number, boolean toUpperCase) {
        return (toUpperCase ? name.toUpperCase() : name) + number;
    }

    public String foo(String name, int number) {
        return foo(name, number, false);
    }

    public String foo(String name, boolean toUpperCase) {
        return foo(name, defaultNumber, toUpperCase);
    }

    public String foo(String name) {
        return foo(name, defaultNumber);
    }
}

所有的重載都是解決一個(gè)問題,字符串和數(shù)字的拼接复斥,并且需要說明字母是否轉(zhuǎn)換為大寫营密,默認(rèn)是不轉(zhuǎn)換。Kotlin的實(shí)現(xiàn):

fun foo(name: String, number: Int = 42, toUpperCase: Boolean = false): String {
    val upCaseName = if (toUpperCase) name.toUpperCase() else name
    return upCaseName+number.toString()
}

精簡一下:

fun foo(name: String, number: Int = 42, toUpperCase: Boolean = false): String  
        = (if (toUpperCase) name.toUpperCase() else name)+number

4.Lambdas

這個(gè)任務(wù)的要求是用Kotlin重寫JavaCode4.task4()函數(shù)目锭,不允許使用Iterables類评汰,可以通過Intellij IDEA的代碼補(bǔ)全來選擇合適的方法纷捞。

Java版本的代碼:

public class JavaCode4 extends JavaCode {
    public boolean task4(Collection<Integer> collection) {
        return Iterables.any(collection, new Predicate<Integer>() {
            @Override
            public boolean apply(Integer element) {
                return element % 42 == 0;
            }
        });
    }
}

就是判斷列表中是否包含42整數(shù)倍的元素,先實(shí)現(xiàn)功能:

fun task4(collection: Collection<Int>): Boolean  {
    val devide42: (Int) -> Boolean = { x -> x % 42 == 0 }
    return collection.filter(devide42).isEmpty().not()
}

這里使用了Collection的filter函數(shù)被去。這個(gè)任務(wù)主要學(xué)習(xí)Kotlin中Lambda表達(dá)式的知識(shí)主儡,簡單來說:

  • lambda表達(dá)式總是用大括弧包起來
  • 它的參數(shù)(如果有的話)在->符號(hào)前面聲明(參數(shù)類型可以省略)
  • 函數(shù)體寫在->符號(hào)后面

在Kotlin中,如果一個(gè)函數(shù)的最后一個(gè)參數(shù)是一個(gè)函數(shù)惨缆,并且你在調(diào)用該函數(shù)時(shí)最后一個(gè)參數(shù)傳遞的是一個(gè)lambda表達(dá)式糜值,那么可以將這個(gè)lambda表達(dá)式寫在括弧外面:

fun task4(collection: Collection<Int>): Boolean  {
    return collection.filter(){ x -> x % 42 == 0 }.isEmpty().not()
}

如果只有l(wèi)ambda表達(dá)式這一個(gè)參數(shù),那么括弧也可以省略:

fun task4(collection: Collection<Int>): Boolean  {
    return collection.filter{ x -> x % 42 == 0 }.isEmpty().not()
}

如果lambda表達(dá)式也只有一個(gè)參數(shù)坯墨,那么這個(gè)參數(shù)連同->符號(hào)也可以省略寂汇,直接將它命名為it

fun task4(collection: Collection<Int>): Boolean = 
    collection.filter { it%42 ==0 }.isNotEmpty()

5.String Templates

任務(wù)要求生成一個(gè)正則表達(dá)式,可以匹配'13 JUN 1992'這樣格式的字符串捣染。
主要是學(xué)習(xí)Kotlin的各種字符串模板骄瓣,Kotlin中字符串有兩種,通過 一對(duì)"包起來自字符串耍攘,這里可以支持轉(zhuǎn)義字符榕栏。如:

val s = "Hello, world!\n"

或者通過一對(duì)"""包起來的字符串,如:

val text = """
    for (c in "foo")
        print(c)
"""

字符串還可以包含模板表達(dá)式(template expressions)蕾各,如:

val i = 10
val s = "i = $i" // evaluates to "i = 10"

val s1 = "abc"
val str = "$s1.length is ${s1.length}" // evaluates to "abc.length is 3"

val price = """
${'$'}9.99
"""

回到我們的任務(wù)本身扒磁,任務(wù)里面已經(jīng)給了足夠的提示,完成起來也比較容易:

val month = "(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)"
fun task5(): String = """\d{2} ${month} \d{4}"""

6.Data Class

任務(wù)要求將JavaCode6.Person類轉(zhuǎn)換成Kotlin示损。先來看看Java源碼:

public class JavaCode6 extends JavaCode {

    public static class Person {
        private final String name;
        private final int age;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public int getAge() {
            return age;
        }
    }
}

Kotlin實(shí)現(xiàn):

data class Person(val name: String, val age: Int)

Kotlin中Data class對(duì)應(yīng)Java中的實(shí)體類渗磅,需要在定義類時(shí)標(biāo)明為data,編譯器會(huì)自動(dòng)根據(jù)主構(gòu)造函數(shù)中定義的屬性生成下面這些成員函數(shù):

  • equals()函數(shù)和hashCode()函數(shù)

  • toString()函數(shù)检访,返回的形式為"Person(name=TangSir, age=28)"

  • componentN()函數(shù)始鱼,componentN()具體返回的值根據(jù)類定義時(shí)屬性定義的屬性決定。如:

     val name = person.component1()
     val age = person.component2()
    
  • copy()函數(shù)

但是如果上面列出來的函數(shù)已經(jīng)在類的實(shí)現(xiàn)中顯式定義或者繼承了父類相應(yīng)的函數(shù)脆贵,編譯器則不會(huì)生成相應(yīng)的函數(shù)医清。

為了保持一致性以及編譯器所生成代碼具有意義,data class必須滿足以下這些條件:

  • 主構(gòu)造函數(shù)至少有一個(gè)參數(shù)
  • 主構(gòu)造函數(shù)中的所有參數(shù)都必須定義為val或者var
  • Data class不能是abstract, open, sealed or inner

7.Nullable Type

任務(wù)要求將ava版本sendMessageToClient用Kotlin實(shí)現(xiàn)卖氨,只允許使用一個(gè)if語句:

public void sendMessageToClient(@Nullable Client client, @Nullable String message, @NotNull Mailer mailer) {
        if (client == null || message == null) return;

        PersonalInfo personalInfo = client.getPersonalInfo();
        if (personalInfo == null) return;

        String email = personalInfo.getEmail();
        if (email == null) return;

        mailer.sendMessage(email, message);
    }

這是我們常見的防御式編程会烙,處處都要考慮變量是否為null的情況。

Kotlin對(duì)null的保護(hù)總結(jié)為以下幾點(diǎn):

  • 如果一個(gè)變量可能為null筒捺,那么在定義的時(shí)候在類型后面加上?
    val a: Int? = null

  • 對(duì)于一個(gè)可能為null的變量柏腻,如果必須在其值不為null時(shí)才進(jìn)行后續(xù)操作,那么可以使用?.操作符來保護(hù)系吭,下面的兩種表示方式是等效的五嫂,即a為null時(shí)什么都不做:

    val a: Int? = null
    ...
    //if statment
    if (a != null){
        a.toLong()
    }
    
    //?.operator
    a?.toLong()
    
  • 當(dāng)一個(gè)變量為null時(shí),如果我們想為它提供一個(gè)替代值,那么可以使用?:
    val myLong = a?.toLong() ?: 0L
    上面的語句的意思就是如果a確實(shí)為null沃缘,那么將myLong賦值為0

  • 最后一條躯枢,就是對(duì)于如果一個(gè)可能是null,如果我們可以確保它已經(jīng)不是null槐臀,那么可以使用!!操作符锄蹂。但是不推薦這么使用。!!是壞代碼的味道水慨。

    val a: Int? = null
    ...
    a!!.toLong() 
    

回到我們的任務(wù)得糜,由于只允許使用一個(gè)if語句,官方的參考答案是這樣的:

fun sendMessageToClient(
       client: Client?, message: String?, mailer: Mailer
) {
   val email = client?.personalInfo?.email
   if (email != null && message != null) {
       mailer.sendMessage(email, message)
   }
}

8.Smart Casts

任務(wù)要求使用Kotlin的Smart Cast和When表達(dá)式重新實(shí)現(xiàn)JavaCode8.eval()函數(shù):

public class JavaCode8 extends JavaCode {
    public int eval(Expr expr) {
        if (expr instanceof Num) {
            return ((Num) expr).getValue();
        }
        if (expr instanceof Sum) {
            Sum sum = (Sum) expr;
            return eval(sum.getLeft()) + eval(sum.getRight());
        }
        throw new IllegalArgumentException("Unknown expression");
    }
}

也是我們常見的強(qiáng)制類型轉(zhuǎn)換讥巡。

在Kotlin中掀亩,多數(shù)情況下我么不需要顯式的使用強(qiáng)制轉(zhuǎn)換,因?yàn)榫幾g器會(huì)為不可變的變量帶入is檢查欢顷,并且在必要的時(shí)候自動(dòng)插入(安全的)強(qiáng)制轉(zhuǎn)換槽棍。is通常和when表達(dá)式一起搭配使用:

when (x) {
    is Int -> print(x + 1)
    is String -> print(x.length + 1)
    is IntArray -> print(x.sum())
}

when和Java中的switch/case語句相似,但是要強(qiáng)大的多抬驴。主要區(qū)別在于when語句的參數(shù)可以是任何類型炼七,其所有分支的判斷條件也可以是任何類型的。

when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> { // Note the block
        print("x is neither 1 nor 2")
    }
}

when可以作為一個(gè)語句也可以作為一個(gè)表達(dá)式布持,如果作為一個(gè)表達(dá)式使用豌拙,它可以有返回值,所以必須覆蓋所有可能的條件或者實(shí)現(xiàn)else分支题暖,否則編譯器會(huì)報(bào)錯(cuò)按傅。

val result = when (x){
    0, 1 -> "binary"
    else -> "error"
}

除此之外,when的條件語句還有很多其他的表達(dá)方式胧卤,如判斷范圍等唯绍,可以參考官方文檔。

回到任務(wù)的解決:

fun eval(e: Expr): Int =
        when (e) {
            is Num -> e.value
            is Sum -> eval(e.left) + eval(e.right)
            else -> throw IllegalArgumentException("Unknown expression")
        }

9.Extension Functions

Kotlin中函數(shù)擴(kuò)展就是不修改一個(gè)類的源碼(通常是我們沒有源碼枝誊,無法修改)的情況下况芒,通過擴(kuò)展函數(shù)為一個(gè)類添加一個(gè)新的功能。擴(kuò)展函數(shù)在行為好像它屬于被擴(kuò)展的類叶撒,所以在擴(kuò)展函數(shù)中我們可以使用this以及所有被擴(kuò)展類的公有方法绝骚。

任務(wù)要求為IntPair<Int, Int>分別實(shí)現(xiàn)一個(gè)擴(kuò)展函數(shù)r()r()函數(shù)的功能就是創(chuàng)建一個(gè)有理數(shù)祠够。

data class RationalNumber(val numerator: Int, val denominator: Int)

fun Int.r(): RationalNumber = RationalNumber(this, 1)
fun Pair<Int, Int>.r(): RationalNumber = RationalNumber(this.first,this.second)

Pair的擴(kuò)展函數(shù)r中this可以省略:

fun Pair<Int, Int>.r(): RationalNumber = RationalNumber(first,second)

10.Object Expression

Kotlin中當(dāng)我們需要?jiǎng)?chuàng)建某一個(gè)類的對(duì)象并且只需要對(duì)該對(duì)象做一點(diǎn)小的修改压汪,而不需要重新創(chuàng)建一個(gè)子類時(shí)可以使用object expression。和Java中的匿名內(nèi)部類很相似古瓤。

任務(wù)的要求是創(chuàng)建一個(gè)比較器(comparator)蛾魄,提供給Collection類對(duì)list按照降序排序。先來實(shí)現(xiàn)功能:

<pre>
fun task10(): List<Int> {
val arrayList = arrayListOf(1, 5, 2)
Collections.sort(arrayList,object: Comparator<Int>{
override fun compare(x: Int, y: Int): Int {
return y-x
}
}
)
return arrayList
}
</pre>

加粗部分就是所謂的object expression,修改為lambda表達(dá)式(哦哦滴须,這好像是Task11的任務(wù)要求)

<pre>
fun task10(): List<Int> {
val arrayList = arrayListOf(1, 5, 2)
Collections.sort(arrayList) { x, y -> y - x }
return arrayList
}
</pre>

這個(gè)任務(wù)還展示了Kotlin和Java之間的交互,因?yàn)?code>task10()函數(shù)中Collections是一個(gè)Java類叽奥。

11.SAM Conversions

所謂SAM conversions就是如果一個(gè)object實(shí)現(xiàn)了一個(gè)SAM(Single Abstract Method)接口時(shí)扔水,可以直接傳遞一個(gè)lambda表達(dá)式,代碼實(shí)現(xiàn)在上面朝氓。

12.Extensions On Collections

在Kotlin的標(biāo)準(zhǔn)庫提供了許多擴(kuò)展函數(shù)使得集合的使用非常方便魔市。由于Kotlin可以很容易的和Java代碼混合使用,所有Kotlin直接是使用標(biāo)準(zhǔn)的Java集合類(做了細(xì)小的改進(jìn))赵哲。

本任務(wù)的要求就是使用擴(kuò)展函數(shù)sortedDescending重寫上一個(gè)任務(wù)中的代碼:

fun task12(): List<Int> {
    return arrayListOf(1, 5, 2).sortedDescending()
}

好了以上就是Kotlin Koans第一部分全部13個(gè)任務(wù)待德。

下一篇:Kotlin Koans學(xué)習(xí)筆記(2)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市枫夺,隨后出現(xiàn)的幾起案子将宪,更是在濱河造成了極大的恐慌,老刑警劉巖橡庞,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件较坛,死亡現(xiàn)場離奇詭異,居然都是意外死亡扒最,警方通過查閱死者的電腦和手機(jī)丑勤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吧趣,“玉大人法竞,你說我怎么就攤上這事∏看欤” “怎么了岔霸?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長纠拔。 經(jīng)常有香客問我秉剑,道長,這世上最難降的妖魔是什么稠诲? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任侦鹏,我火速辦了婚禮,結(jié)果婚禮上臀叙,老公的妹妹穿的比我還像新娘略水。我一直安慰自己,他們只是感情好劝萤,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布渊涝。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪跨释。 梳的紋絲不亂的頭發(fā)上胸私,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音鳖谈,去河邊找鬼岁疼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛缆娃,可吹牛的內(nèi)容都是我干的捷绒。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼贯要,長吁一口氣:“原來是場噩夢啊……” “哼暖侨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起崇渗,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤字逗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后显押,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扳肛,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年乘碑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了挖息。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡兽肤,死狀恐怖套腹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情资铡,我是刑警寧澤电禀,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站笤休,受9級(jí)特大地震影響尖飞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜店雅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一政基、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧闹啦,春花似錦沮明、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽酱畅。三九已至,卻和暖如春江场,著一層夾襖步出監(jiān)牢的瞬間纺酸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國打工扛稽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吁峻,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓在张,卻偏偏與公主長得像,于是被迫代替她去往敵國和親矮慕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子帮匾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容