​JDK1.8新特性(八):還在重複寫空指針檢查代碼?趕緊使用Optional吧!​

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"1、前言"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作爲一名Java程序員,無論是初入茅廬的菜鳥,還是久經江湖的高手,曾經肯定遭遇過各種各樣的異常錯誤。在國外的一篇文章中,就統計了關於異常類型的排行榜,如下圖:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/63/63aa883a2b7aabf121b3101f85ac2f2a.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"是的,你沒有看錯,"},{"type":"codeinline","content":[{"type":"text","text":"NullPointerException"}]},{"type":"text","text":"位居榜首。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Null Reference"}]},{"type":"text","text":"的發明者Charles Antony Richard Hoare說過:“我稱之爲我的十億美元錯誤。這是1965年發明空引用的結果……這導致了無數的錯誤,漏洞和系統崩潰,在最近40年中可能造成十億美元的痛苦和破壞。”"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這看起來有些誇張,但毫無爭議的是"},{"type":"codeinline","content":[{"type":"text","text":"NullPointerException"}]},{"type":"text","text":"簡直就是程序員心中的痛,並不是說它有多難以解決,而是爲了解決它我們需要再付出了額外代價。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"還記得當初剛入行時候的你,三天兩頭碰到"},{"type":"codeinline","content":[{"type":"text","text":"NullPointerException"}]},{"type":"text","text":"而引發的bug,解決完一個,又在另一個地方遇到。這也慢慢讓你懂得,不要相信任何“對象”,特別是別人提供給你的,在使用的地方都加上判斷,這樣就放心多了。於是代碼通常就變成了下面這樣:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"String name = \"Unknown\";\nif (null != people) {\n\tif (null != people.getName()) {\n \tname = people.getName();\n }\n}\nreturn name;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣處理,雖然不用擔心"},{"type":"codeinline","content":[{"type":"text","text":"NullPointerException"}]},{"type":"text","text":"了,但是過多的判斷語句着實讓人頭皮發麻,代碼變得臃腫不堪。如果對象過於複雜,對象裏面還有對象等等,你還要繼續逐層判斷麼?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"令人興奮的是,JDK1.8引入了一個新類"},{"type":"codeinline","content":[{"type":"text","text":"java.util.Optional"}]},{"type":"text","text":",憑藉Optional類提供的API,我們再也不用擔心"},{"type":"codeinline","content":[{"type":"text","text":"NullPointerException"}]},{"type":"text","text":"了,更不會再去寫那些煩人的判斷啦。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"2、Optional類"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"舉例來說,使用新類意味着,如果你知道一個人可能有也可能沒有車,那麼Person類內部的car變量就不應該聲明爲Car,遭遇某人沒有車時把null引用賦值給它,而是應該像下圖這樣直接將其聲明爲Optional類型。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/45/45319b0759653d515cfd10b9044f400a.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"變量存在時,"},{"type":"codeinline","content":[{"type":"text","text":"Optional"}]},{"type":"text","text":"類只是對類簡單封裝。變量不存在時,缺失的值會被建模成一個“空”的"},{"type":"codeinline","content":[{"type":"text","text":"Optional"}]},{"type":"text","text":"對象,由方法"},{"type":"codeinline","content":[{"type":"text","text":"Optional.empty()"}]},{"type":"text","text":"返回。"},{"type":"codeinline","content":[{"type":"text","text":"Optional.empty()"}]},{"type":"text","text":"方法是一個靜態工廠方法,它返回Optional類的特定單一實例。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Optional"}]},{"type":"text","text":",本質上是一個容器對象,擁有一個非空值或空值,需要我們將對象實例傳入該容器中。如果值存在,"},{"type":"codeinline","content":[{"type":"text","text":"Optional.isPresent()"}]},{"type":"text","text":"方法返回"},{"type":"codeinline","content":[{"type":"text","text":"true"}]},{"type":"text","text":",並通過"},{"type":"codeinline","content":[{"type":"text","text":"Optional.get()"}]},{"type":"text","text":"方法獲取值。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Optional"}]},{"type":"text","text":"的構造方法爲"},{"type":"codeinline","content":[{"type":"text","text":"private"}]},{"type":"text","text":",無法直接使用new來創建"},{"type":"codeinline","content":[{"type":"text","text":"Optional"}]},{"type":"text","text":"對象,只能使用"},{"type":"codeinline","content":[{"type":"text","text":"Optional"}]},{"type":"text","text":"提供的靜態方法創建。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Optional提供的創建方法如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Optional.of(obj)"}]},{"type":"text","text":":如果對象爲 "},{"type":"codeinline","content":[{"type":"text","text":"null"}]},{"type":"text","text":",將會拋出"},{"type":"codeinline","content":[{"type":"text","text":"NullPointerException"}]},{"type":"text","text":"。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Optional.ofNullable(obj)"}]},{"type":"text","text":":如果對象爲 null,將會創建不包含值的 "},{"type":"text","marks":[{"type":"strong"}],"text":"EMPTY"},{"type":"text","text":" "},{"type":"codeinline","content":[{"type":"text","text":"Optional"}]},{"type":"text","text":"對象實例(new Optional<>())。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Optional.empty()"}]},{"type":"text","text":" :等同於 "},{"type":"codeinline","content":[{"type":"text","text":"Optional.ofNullable(null)"}]},{"type":"text","text":"。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中,源碼片段如下:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"/**\n * Constructs an instance with the value present.\n *\n * @param value the non-null value to be present\n * @throws NullPointerException if value is null\n */\nprivate Optional(T value) {\n this.value = Objects.requireNonNull(value);\n}\n\n……\n\n/**\n * Returns an {@code Optional} with the specified present non-null value.\n *\n * @param the class of the value\n * @param value the value to be present, which must be non-null\n * @return an {@code Optional} with the value present\n * @throws NullPointerException if value is null\n */\npublic static Optional of(T value) {\n return new Optional<>(value);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"/**\n * Returns an {@code Optional} describing the specified value, if non-null,\n * otherwise returns an empty {@code Optional}.\n *\n * @param the class of the value\n * @param value the possibly-null value to describe\n * @return an {@code Optional} with a present value if the specified value\n * is non-null, otherwise an empty {@code Optional}\n */\npublic static Optional ofNullable(T value) {\n return value == null ? empty() : of(value);\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"/**\n * Common instance for {@code empty()}.\n */\nprivate static final Optional> EMPTY = new Optional<>();\n\n……\n\n/**\n * Returns an empty {@code Optional} instance. No value is present for this\n * Optional.\n *\n * @apiNote Though it may be tempting to do so, avoid testing if an object\n * is empty by comparing with {@code ==} against instances returned by\n * {@code Option.empty()}. There is no guarantee that it is a singleton.\n * Instead, use {@link #isPresent()}.\n *\n * @param Type of the non-existent value\n * @return an empty {@code Optional}\n */\npublic static Optional empty() {\n @SuppressWarnings(\"unchecked\")\n Optional t = (Optional) EMPTY;\n return t;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"強烈建議使用"},{"type":"codeinline","content":[{"type":"text","text":"Optional.ofNullable(obj)"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"方法,來創建"},{"type":"codeinline","content":[{"type":"text","text":"Optional"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"對象,並獲取對應值。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"3、Optional的使用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"到目前爲止,你已經知道"},{"type":"codeinline","content":[{"type":"text","text":"Optional"}]},{"type":"text","text":"的好處了吧,但是,我們該如何使用呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用可接受null的"},{"type":"codeinline","content":[{"type":"text","text":"Optional"}]},{"type":"text","text":"對象,即:使用靜態工程方法"},{"type":"codeinline","content":[{"type":"text","text":"Optional.ofNullable(obj)"}]},{"type":"text","text":",創建一個可以允許null值的"},{"type":"codeinline","content":[{"type":"text","text":"Optional"}]},{"type":"text","text":"對象:"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"Optional optional = Optional.ofNullable(people);"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"即使people是null,optional對象也就是個空對象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果people不爲null,根據"},{"type":"codeinline","content":[{"type":"text","text":"Optional.isPresent()"}]},{"type":"text","text":"方法返回"},{"type":"codeinline","content":[{"type":"text","text":"true"}]},{"type":"text","text":",並通過"},{"type":"codeinline","content":[{"type":"text","text":"Optional.get()"}]},{"type":"text","text":"方法獲取值。"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了避免NPE,"},{"type":"codeinline","content":[{"type":"text","text":"Optional.isPresent()"}]},{"type":"text","text":"方法已經對null進行了判斷,若存在返回true。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"People p = null;\nif (optional.isPresent()) {\n p = optional.get();\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看到這裏,你可能會發現這與null判斷檢查並無差異。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"後來接觸到"},{"type":"codeinline","content":[{"type":"text","text":"Optional"}]},{"type":"text","text":"其他API,我才發現真正體現它價值的是下面這些API。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3.1 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong"}],"text":"Optional.map"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從對象中獲取某個屬性,是最常見的操作。比如,你可能需要從people對象中獲取人名。在獲取人名之前,你需要檢查people對象是否爲null,如下所示:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"String name = null;\nif (null != people) {\n name = people.getName();\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用"},{"type":"codeinline","content":[{"type":"text","text":"Optional.map"}]},{"type":"text","text":"方法,可以這麼寫:"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"Optional optional = Optional.ofNullable(people);\nOptional stringOptional = optional.map(People::getName);"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3.2 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong"}],"text":"Optional.orElse"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當一個對象爲 null 時,業務上通常可以設置一個默認值,從而使流程繼續下去。"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"String name = null != people ? people.getName() : \"Unknown\";"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"或者拋出一個異常。"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"if (null != people.getName()) {\n throw new RuntimeException();\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Optional"}]},{"type":"text","text":" 類提供兩個方法 "},{"type":"codeinline","content":[{"type":"text","text":"orElse"}]},{"type":"text","text":" 與 "},{"type":"codeinline","content":[{"type":"text","text":"orElseThrow"}]},{"type":"text","text":" ,可以方便完成上面轉化。"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"// 設置默認值\nString name = optional.orElse(new People(\"Unknown\")).getName();\n// 拋出異常\nString name = optional.orElseThrow(RuntimeException::new).getName();"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果 "},{"type":"codeinline","content":[{"type":"text","text":"optional"}]},{"type":"text","text":" 爲空,提供默認值或拋出異常。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3.3 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong"}],"text":"Optional.filter"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你經常需要調用某個對象的方法,查看它的某些屬性。比如,你可能需要檢查人名是否爲“xcbeyond”。爲了以一種安全的方式進行這些操作,你首先需要判斷people對象是否爲null,再調用它的方法getName,如下所示:"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"if (null != people && \"xcbeyond\".equals(people.getName())) {\n\tSystem.out.println(\"ok\");\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用Optional類提供的方法filter,可以很好的重構:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"optional.filter(people1 -> \"xcbeyond\".equals(people.getName()))\n\t\t.ifPresent(x -> System.out.print(\"ok\"));"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"4、Optional重構代碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"讓我們一起再看看文章開頭的代碼:"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"String name = \"Unknown\";\nif (null != people) {\n\tif (null != people.getName()) {\n \tname = people.getName();\n }\n}\nreturn name;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在知道了Optional之後,進行代碼重構:"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"Optional optional = Optional.ofNullable(people);\nreturn optional.map(People::getName).orElse(\"Unknown\");"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"結合Optional、Lambda表達式,可以明顯看到重構之後,使得代碼更加流暢連貫,並且提高代碼整體可讀性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"參考文章:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"1."},{"type":"link","attrs":{"href":"https://dzone.com/articles/the-top-10-exception-types-in-production-java-appl","title":null},"content":[{"type":"text","text":"https://dzone.com/articles/the-top-10-exception-types-in-production-java-appl"}],"marks":[{"type":"italic"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"2.《Java 8實戰》"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/22/220b80503e4a0d5ba95bb4523329a4f2.gif","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章