บทที่ 3 สตริง ตัวเปรียบเทียบ และตัวกรอง
วิธีการบางอย่างที่ JDK นำมาใช้นั้นมีประโยชน์มากสำหรับการเขียนโค้ดสไตล์การทำงาน เราคุ้นเคยกับคลาสและอินเทอร์เฟซบางอย่างในไลบรารี JDK เป็นอย่างดี เช่น String เพื่อกำจัดรูปแบบเก่าที่เราคุ้นเคย เราต้องกระตือรือร้นมองหาโอกาสในการใช้วิธีการใหม่เหล่านี้ ในทำนองเดียวกัน เมื่อเราจำเป็นต้องใช้คลาสภายในที่ไม่ระบุชื่อด้วยวิธีเดียวเท่านั้น ตอนนี้เราสามารถแทนที่มันด้วยนิพจน์ lambda ได้โดยไม่ต้องเขียนให้ยุ่งยากเหมือนเมื่อก่อน
ในบทนี้ เราจะใช้นิพจน์แลมบ์ดาและการอ้างอิงเมธอดเพื่อสำรวจสตริง ใช้อินเทอร์เฟซตัวเปรียบเทียบ ดูไฟล์ในไดเร็กทอรี และติดตามการเปลี่ยนแปลงในไฟล์และไดเร็กทอรี วิธีการบางอย่างที่แนะนำในบทที่แล้วจะยังคงปรากฏอยู่ที่นี่เพื่อช่วยให้เราทำงานเหล่านี้ได้ดียิ่งขึ้น เทคนิคใหม่ที่คุณเรียนรู้จะช่วยเปลี่ยนโค้ดที่ยาวและน่าเบื่อให้เป็นสิ่งที่กระชับ นำไปใช้ได้รวดเร็ว และบำรุงรักษาง่าย
วนซ้ำบนสตริง
เมธอด chars() เป็นเมธอดใหม่ในคลาส String ซึ่งเป็นส่วนหนึ่งของอินเทอร์เฟซ CharSequence มันเป็นเครื่องมือที่มีประโยชน์มากถ้าคุณต้องการสำรวจลำดับอักขระของ String อย่างรวดเร็ว ด้วยตัววนซ้ำภายในนี้ เราสามารถดำเนินการกับอักขระแต่ละตัวในสตริงได้อย่างสะดวก ลองใช้มันเพื่อประมวลผลสตริงก่อน ต่อไปนี้เป็นวิธีการใช้การอ้างอิงวิธีการ
คัดลอกรหัสรหัสดังต่อไปนี้:
สตริงสุดท้าย str = "w00t";
str.chars()
.forEach(ch -> System.out.println(ch));
chars() วิธีการส่งคืนวัตถุ Stream ซึ่งเราสามารถใช้ตัววนซ้ำภายใน forEach() เพื่อสำรวจ ในตัววนซ้ำ เราสามารถเข้าถึงอักขระในสตริงได้โดยตรง ด้านล่างนี้คือผลลัพธ์ของการวนซ้ำผ่านสตริงและการพิมพ์อักขระแต่ละตัว
คัดลอกรหัสรหัสดังต่อไปนี้:
119
48
48
116
นี่ไม่ใช่ผลลัพธ์ที่เราต้องการ เราคาดว่าจะเห็นตัวอักษร แต่ผลลัพธ์เป็นตัวเลข นี่เป็นเพราะว่า chars() วิธีการส่งกลับกระแสข้อมูลจำนวนเต็มแทนประเภทอักขระ ก่อนอื่นมาทำความเข้าใจ API นี้ก่อนแล้วจึงปรับผลลัพธ์เอาต์พุตให้เหมาะสม
ในโค้ดก่อนหน้านี้ เราได้สร้างนิพจน์แลมบ์ดาเป็นพารามิเตอร์อินพุตของเมธอด forEach เพียงส่งพารามิเตอร์ไปยังเมธอด println() เนื่องจากการดำเนินการนี้เป็นเรื่องปกติ เราจึงสามารถใช้ Java Compiler เพื่อทำให้โค้ดนี้ง่ายขึ้นได้ เช่นเดียวกับในการใช้การอ้างอิงเมธอดในหน้า 25 ให้แทนที่ด้วยการอ้างอิงเมธอด และปล่อยให้คอมไพลเลอร์กำหนดเส้นทางพารามิเตอร์ให้เรา
เราได้เห็นวิธีการสร้างวิธีการอ้างอิงถึงวิธีการตัวอย่าง ตัวอย่างเช่น เมธอด name.toUpperCase() การอ้างอิงเมธอดคือ String::toUpperCase ในตัวอย่างต่อไปนี้ เรากำลังเรียกวิธีการอินสแตนซ์ที่อ้างอิง System.out แบบคงที่ ด้านซ้ายของเครื่องหมายโคลอนสองตัวที่เมธอดอ้างอิงอาจเป็นชื่อคลาสหรือนิพจน์ก็ได้ ด้วยความยืดหยุ่นนี้ เราสามารถสร้างการอ้างอิงไปยังเมธอด println() ได้อย่างง่ายดาย ดังตัวอย่างด้านล่าง
คัดลอกรหัสรหัสดังต่อไปนี้:
str.chars()
.forEach(System.out::println);
อย่างที่คุณเห็น คอมไพเลอร์ Java สามารถดำเนินการกำหนดเส้นทางพารามิเตอร์ได้อย่างชาญฉลาดมาก โปรดจำไว้ว่านิพจน์แลมบ์ดาและการอ้างอิงวิธีการสามารถปรากฏได้เฉพาะเมื่อได้รับอินเทอร์เฟซการทำงานเท่านั้น และคอมไพเลอร์ Java จะสร้างวิธีการที่เกี่ยวข้องที่นั่น (คำอธิบายประกอบ: คอมไพเลอร์จะสร้างการใช้งานอินเทอร์เฟซการทำงานซึ่งมีเพียงวิธีเดียวเท่านั้น) วิธีที่เราใช้ก่อนหน้านี้อ้างถึง String::toUpperCase และพารามิเตอร์ที่ส่งผ่านไปยังวิธีการที่สร้างขึ้นจะกลายเป็นอ็อบเจ็กต์เป้าหมายของการเรียกเมธอดในที่สุด เช่นนี้: parameter.toUpperCase() เนื่องจากการอ้างอิงวิธีการจะขึ้นอยู่กับชื่อคลาส (สตริง) การอ้างอิงวิธีการในตัวอย่างข้างต้นจะขึ้นอยู่กับนิพจน์ ซึ่งเป็นอินสแตนซ์ของ PrintStream และมีการอ้างอิงผ่าน System.out เนื่องจากอ็อบเจ็กต์ของการเรียกเมธอดมีอยู่แล้ว คอมไพเลอร์ Java จึงตัดสินใจใช้พารามิเตอร์ในเมธอดที่สร้างขึ้นเป็นพารามิเตอร์ของเมธอด println นี้: System.out.println(name)
(คำอธิบายประกอบ: ในความเป็นจริง มีสองสถานการณ์ส่วนใหญ่ การอ้างอิงเมธอดก็ถูกส่งผ่านเช่นกัน อันแรกคือวัตถุที่สำรวจ แน่นอนว่าเป็นอ็อบเจ็กต์เป้าหมายของการเรียกเมธอด เช่น name.toUpperCase และอีกอันถูกใช้เป็นพารามิเตอร์ของ การเรียกเมธอด เช่น System.out.println(name).)
โค้ดจะง่ายกว่ามากหลังจากใช้การอ้างอิงวิธีการ แต่เราจำเป็นต้องมีความเข้าใจอย่างลึกซึ้งเกี่ยวกับวิธีการทำงาน เมื่อเราคุ้นเคยกับการอ้างอิงเมธอดแล้ว เราก็จะสามารถหาการกำหนดเส้นทางพารามิเตอร์ได้ด้วยตัวเอง
แม้ว่าโค้ดในตัวอย่างนี้จะกระชับเพียงพอ แต่ผลลัพธ์ก็ยังไม่เป็นที่น่าพอใจ เราคาดว่าจะเห็นตัวอักษรแต่ตัวเลขกลับปรากฏขึ้นแทน เพื่อแก้ปัญหานี้ เรามาเขียนวิธีการส่งออก int เป็นตัวอักษรกันดีกว่า
คัดลอกรหัสรหัสดังต่อไปนี้:
โมฆะคงที่ส่วนตัว printChar (int aChar) {
System.out.println((ถ่าน)(aChar));
-
การใช้การอ้างอิงวิธีการสามารถปรับผลลัพธ์ผลลัพธ์ให้เหมาะสมได้อย่างง่ายดาย
คัดลอกรหัสรหัสดังต่อไปนี้:
str.chars()
.forEach(IterateString::printChar);
ถึงแม้ว่าผลลัพธ์ที่ส่งกลับมาด้วย chars() จะเป็น int ก็ไม่สำคัญ เมื่อเราต้องการพิมพ์ เราจะแปลงมันเป็นตัวอักษร คราวนี้ผลลัพธ์เป็นตัวอักษรในที่สุด
คัดลอกรหัสรหัสดังต่อไปนี้:
ว
0
0
ที
หากเราต้องการประมวลผลอักขระตั้งแต่ต้นแทนที่จะเป็น ints เราสามารถแปลง ints ให้เป็นอักขระได้โดยตรงหลังจากการเรียกตัวอักษร:
คัดลอกรหัสรหัสดังต่อไปนี้:
str.chars()
.mapToObj(ch -> Character.valueOf((char)ch))
.forEach(System.out::println);
ที่นี่เราใช้ตัววนซ้ำภายในของ Stream ที่ส่งคืนด้วยตัวอักษร () แน่นอนว่าสามารถใช้ได้มากกว่าวิธีนี้ หลังจากได้รับออบเจ็กต์ Stream แล้ว เราก็สามารถใช้เมธอดของมันได้ เช่น map(), filter(), ลด() เป็นต้น เราสามารถใช้เมธอด filter() เพื่อกรองอักขระที่เป็นตัวเลขออก:
คัดลอกรหัสรหัสดังต่อไปนี้:
str.chars()
.filter(ch -> Character.isDigit(ch))
.forEach(ch -> printChar(ch));
เมื่อส่งออกด้วยวิธีนี้ เราจะเห็นเฉพาะตัวเลขเท่านั้น:
คัดลอกรหัสรหัสดังต่อไปนี้:
0
0
ในทำนองเดียวกัน นอกเหนือจากการส่งนิพจน์แลมบ์ดาไปยังเมธอด filter() และ forEach() แล้ว เรายังสามารถใช้การอ้างอิงเมธอดได้อีกด้วย
คัดลอกรหัสรหัสดังต่อไปนี้:
str.chars()
.filter(ตัวละคร::isDigit)
.forEach(IterateString::printChar);
การอ้างอิงวิธีการที่นี่จะกำจัดการกำหนดเส้นทางพารามิเตอร์ที่ซ้ำซ้อน ในตัวอย่างนี้ เรายังเห็นการใช้งานที่แตกต่างจากสองวิธีก่อนหน้านี้ด้วย ครั้งแรกที่เราอ้างอิงวิธีการอินสแตนซ์ ครั้งที่สองเป็นวิธีการอ้างอิงแบบคงที่ (System.out) คราวนี้เป็นการอ้างอิงถึงวิธีการคงที่ - การอ้างอิงวิธีการได้รับการจ่ายอย่างเงียบๆ
การอ้างอิงถึงวิธีการอินสแตนซ์และวิธีการแบบคงที่ทั้งหมดมีลักษณะเหมือนกัน: ตัวอย่างเช่น String::toUpperCase และ Character::isDigit คอมไพเลอร์กำหนดว่าวิธีการนั้นเป็นวิธีการแบบอินสแตนซ์หรือวิธีการแบบคงที่เพื่อกำหนดวิธีการกำหนดเส้นทางพารามิเตอร์ หากเป็นวิธีการตัวอย่าง มันจะใช้พารามิเตอร์อินพุตของวิธีการที่สร้างขึ้นเป็นวัตถุเป้าหมายของการเรียกวิธีการ เช่น พารามิเตอร์ toUpperCase(); (แน่นอนว่ามีข้อยกเว้น เช่น วัตถุเป้าหมายของการเรียกวิธีการ) ได้รับการระบุแล้ว เช่น System::out.println ()) นอกจากนี้ หากเป็นวิธีแบบคงที่ พารามิเตอร์อินพุตของวิธีที่สร้างจะถูกนำมาใช้เป็นพารามิเตอร์ของวิธีที่อ้างอิง เช่น Character.isDigit(พารามิเตอร์) ภาคผนวก 2 ในหน้า 152 มีคำแนะนำโดยละเอียดเกี่ยวกับวิธีการใช้การอ้างอิงเมธอดและไวยากรณ์
แม้ว่าการอ้างอิงวิธีการจะสะดวกต่อการใช้งาน แต่ก็ยังมีปัญหา - ความคลุมเครือที่เกิดจากข้อขัดแย้งในการตั้งชื่อวิธีการ หากวิธีการจับคู่เป็นทั้งวิธีอินสแตนซ์และวิธีคงที่ คอมไพลเลอร์จะรายงานข้อผิดพลาดเนื่องจากความคลุมเครือของวิธีการ ตัวอย่างเช่น ถ้าเราเขียน Double::toString แบบนี้ จริงๆ แล้วเราต้องการแปลงประเภท double ให้เป็นสตริง แต่คอมไพเลอร์ไม่รู้ว่าจะเรียกวิธีการอินสแตนซ์ของ public String toString() หรือเรียก public static String toString. (double) เนื่องจากทั้งสองวิธีเป็นคลาส Double หากคุณพบสถานการณ์เช่นนี้ อย่าเพิ่งท้อแท้ เพียงใช้สำนวนแลมบ์ดาเพื่อเติมเต็มสถานการณ์
เมื่อเราพอใจกับการเขียนโปรแกรมเชิงฟังก์ชันแล้ว เราก็สามารถสลับไปมาระหว่างนิพจน์ lambda และการอ้างอิงเมธอดได้ตามต้องการ
ในส่วนนี้เราใช้วิธีใหม่ใน Java 8 เพื่อวนซ้ำสตริง มาดูการปรับปรุงอินเทอร์เฟซตัวเปรียบเทียบกัน