10 ฟังก์ชั่นยอดนิยมใน Java ที่มา:
DZone บทความนี้แสดงรายการคุณสมบัติการเขียนโปรแกรม Java สิบประการที่นักพัฒนามักใช้ในการทำงานประจำวัน
1. วิธีการรวบรวมโรงงาน คอลเลกชันเป็นหนึ่งในคุณสมบัติที่ใช้บ่อยที่สุดในการเขียนโปรแกรม พวกมันถูกใช้เป็นภาชนะที่เราจัดเก็บสิ่งของและส่งต่อ คอลเลกชันยังใช้ในการจัดเรียง ค้นหา และทำซ้ำวัตถุ ทำให้ชีวิตของโปรแกรมเมอร์ง่ายขึ้นมาก มีอินเทอร์เฟซพื้นฐานหลายอย่าง เช่น รายการ ตั้งค่า แผนที่ ตลอดจนการใช้งานหลายอย่าง วิธีดั้งเดิมในการสร้างคอลเลกชั่นและแผนที่อาจดูเหมือนซับซ้อนสำหรับนักพัฒนาหลายคน นั่นเป็นสาเหตุที่ Java 9 แนะนำวิธีการจากโรงงานที่กระชับหลายวิธี
รายการ :
List countries = List . of ( "Bangladesh" , "Canada" , "United States" , "Tuvalu" ) ;
ชุด :
Set countries = Set . of ( "Bangladesh" , "Canada" , "United States" , "Tuvalu" ) ;
แผนที่ :
Map countriesByPopulation = Map . of ( "Bangladesh" , 164_689_383 ,
"Canada" , 37_742_154 ,
"United States" , 331_002_651 ,
"Tuvalu" , 11_792 ) ;
วิธีการแบบโรงงานมีประโยชน์มากเมื่อเราต้องการสร้างคอนเทนเนอร์ที่ไม่เปลี่ยนรูป แต่หากคุณจะสร้างคอลเลกชันที่ไม่แน่นอน ขอแนะนำให้ใช้แนวทางดั้งเดิม
2. การอนุมานประเภทท้องถิ่น การอนุมานประเภท Java 10 เพิ่มสำหรับตัวแปรโลคัล ก่อนหน้านี้ นักพัฒนาจะต้องระบุประเภทสองครั้งเมื่อประกาศและเริ่มต้นออบเจ็กต์ มันเหนื่อยมาก ดูตัวอย่างต่อไปนี้:
Map > properties = new HashMap < > ( ) ;
ประเภทของข้อมูลทั้งสองด้านมีระบุไว้ที่นี่ หากเรากำหนดไว้ในที่เดียว เครื่องอ่านโค้ดและคอมไพเลอร์ Java จะเข้าใจได้ง่ายว่าต้องเป็นประเภท Map การอนุมานประเภทท้องถิ่นทำเช่นนั้น นี่คือตัวอย่าง:
var properties = new HashMap > ( ) ;
ตอนนี้ทุกอย่างถูกเขียนเพียงครั้งเดียวและโค้ดไม่ได้ดูแย่ลงไปกว่านี้มากนัก และเมื่อเราเรียกใช้เมธอดและเก็บผลลัพธ์ไว้ในตัวแปร โค้ดก็จะสั้นลงอีก ตัวอย่าง:
var properties = getProperties ( ) ;
และต่อไป:
var countries = Set . of ( "Bangladesh" , "Canada" , "United States" , "Tuvalu" ) ;
แม้ว่าการอนุมานแบบท้องถิ่นดูเหมือนเป็นคุณลักษณะที่สะดวก แต่บางคนก็วิพากษ์วิจารณ์มัน นักพัฒนาบางคนโต้แย้งว่าสิ่งนี้ลดความสามารถในการอ่าน และนี่สำคัญกว่าความกะทัดรัด
3. นิพจน์สวิตช์ขั้นสูง คำสั่ง switch แบบดั้งเดิมมีอยู่ใน Java มาตั้งแต่เริ่มต้นและชวนให้นึกถึง C และ C++ ในตอนนั้น นี่เป็นเรื่องปกติ แต่เมื่อภาษาพัฒนาขึ้น โอเปอเรเตอร์นี้ไม่ได้เสนอการปรับปรุงใดๆ ให้เราจนกระทั่ง Java 14 แน่นอนว่ามันมีข้อเสียอยู่บ้าง สิ่งที่ฉาวโฉ่ที่สุดคือ
การล้ม เหลว: เพื่อแก้ปัญหานี้ นักพัฒนาจึงใช้คำสั่งแบ่งซึ่งส่วนใหญ่เป็นโค้ดสำเร็จรูป อย่างไรก็ตาม Java 14 ได้เปิดตัวคำสั่ง switch เวอร์ชันปรับปรุงพร้อมรายการฟังก์ชันที่ใหญ่กว่ามาก ตอนนี้เราไม่จำเป็นต้องเพิ่มคำสั่งแบ่งอีกต่อไปและจะช่วยแก้ปัญหาความล้มเหลวได้ นอกจากนี้ คำสั่ง switch ยังสามารถคืนค่าได้ ซึ่งหมายความว่าเราสามารถใช้เป็นนิพจน์และกำหนดให้กับตัวแปรได้
int day = 5 ;
String result = switch ( day) {
case 1 , 2 , 3 , 4 , 5 -> "Weekday" ;
case 6 , 7 -> "Weekend" ;
default -> "Unexpected value: " + day;
} ;
4. บันทึก แม้ว่า Records จะเป็นฟีเจอร์ที่ค่อนข้างใหม่ที่เปิดตัวใน Java 16 แต่นักพัฒนาหลายคนพบว่ามันมีประโยชน์มาก สาเหตุหลักมาจากการสร้างอ็อบเจ็กต์ที่ไม่เปลี่ยนรูป บ่อยครั้งที่เราต้องการวัตถุข้อมูลในโปรแกรมของเราเพื่อจัดเก็บหรือส่งค่าจากวิธีหนึ่งไปยังอีกวิธีหนึ่ง ตัวอย่างเช่นคลาสสำหรับการถ่ายโอนพิกัด x, y และ z ซึ่งเราจะเขียนดังนี้:
package ca. bazlur. playground ;
import java. util. Objects ;
public final class Point {
private final int x;
private final int y;
private final int z;
public Point ( int x, int y, int z) {
this . x = x;
this . y = y;
this . z = z;
}
public int x ( ) {
return x;
}
public int y ( ) {
return y;
}
public int z ( ) {
return z;
}
@Override
public boolean equals ( Object obj) {
if ( obj == this ) return true ;
if ( obj == null || obj. getClass ( ) != this . getClass ( ) ) return false ;
var that = ( Point ) obj;
return this . x == that. x &&
this . y == that. y &&
this . z == that. z;
}
@Override
public int hashCode ( ) {
return Objects . hash ( x, y, z) ;
}
@Override
public String toString ( ) {
return "Point[" +
"x=" + x + ", " +
"y=" + y + ", " +
"z=" + z + ']' ;
}
}
ชั้นเรียนดูละเอียดเกินไป ด้วยความช่วยเหลือของรายการรหัสทั้งหมดนี้สามารถถูกแทนที่ด้วยเวอร์ชันที่กระชับยิ่งขึ้น:
package ca. bazlur. playground ;
public record Point ( int x, int y, int z) {
}
5.ไม่จำเป็น วิธีการคือสัญญาที่เรากำหนดเงื่อนไข เราระบุพารามิเตอร์ตามประเภท เช่นเดียวกับประเภทการส่งคืน จากนั้นเราคาดหวังว่าเมื่อมีการเรียกใช้เมธอดนั้น มันจะทำงานตามสัญญา อย่างไรก็ตาม บ่อยครั้งที่เราจบลงด้วยค่า null จากวิธีการแทนที่จะเป็นค่าประเภทที่ระบุ นี่เป็นความผิดพลาด เพื่อแก้ไขปัญหานี้ โดยทั่วไปตัวเริ่มต้นจะทดสอบค่าด้วยเงื่อนไข if โดยไม่คำนึงว่าค่าจะเป็นโมฆะหรือไม่ ตัวอย่าง:
public class Playground {
public static void main ( String [ ] args) {
String name = findName ( ) ;
if ( name != null ) {
System . out. println ( "Length of the name : " + name. length ( ) ) ;
}
}
public static String findName ( ) {
return null ;
}
}
ดูโค้ดด้านบน
เมธอด findName ควรจะส่งคืน
String แต่จะส่งคืน null ขณะนี้ผู้ริเริ่มต้องตรวจสอบค่าว่างก่อนเพื่อจัดการกับปัญหา หากผู้ริเริ่มลืมทำเช่นนี้ เราก็จะได้รับ
NullPointerException ในทางกลับกัน หากลายเซ็นวิธีการระบุถึงความเป็นไปได้ของการไม่คืนสินค้า สิ่งนี้จะช่วยแก้ไขความสับสนทั้งหมดได้
และนี่คือจุดที่ Optional สามารถช่วยเราได้
import java. util. Optional ;
public class Playground {
public static void main ( String [ ] args) {
Optional optionalName = findName ( ) ;
optionalName. ifPresent ( name -> {
System . out. println ( "Length of the name : " + name. length ( ) ) ;
} ) ;
}
public static Optional findName ( ) {
return Optional . empty ( ) ;
}
}
ที่นี่เราได้เขียน เมธอด
findName ใหม่ ด้วย ตัวเลือก
Optional เพื่อไม่ให้ส่งคืนค่าใดๆ สิ่งนี้จะแจ้งเตือนโปรแกรมเมอร์ล่วงหน้าและแก้ไขปัญหา
6. Java วันที่ เวลา API นักพัฒนาซอฟต์แวร์ทุกคนสับสนกับการคำนวณวันที่และเวลาในระดับหนึ่ง นี่ไม่ใช่การพูดเกินจริง สาเหตุหลักมาจากการขาด Java API ที่ดีสำหรับการทำงานกับวันที่และเวลา ตอนนี้ปัญหานี้ไม่เกี่ยวข้องอีกต่อไปแล้ว เนื่องจาก Java 8 เปิดตัวชุด API ที่ยอดเยี่ยมในแพ็คเกจ java.time ซึ่งแก้ไขปัญหาทั้งหมดที่เกี่ยวข้องกับวันที่และเวลา แพ็คเกจ java.time มีอินเทอร์เฟซและคลาสมากมายที่ช่วยขจัดปัญหาส่วนใหญ่ รวมถึงเขตเวลาด้วย คลาสที่ใช้บ่อยที่สุดในแพ็คเกจนี้คือ:
LocalDate
เวลาท้องถิ่น
LocalDateTime
ระยะเวลา
ระยะเวลา
โซนวันที่และเวลา
ตัวอย่างการใช้คลาสจากแพ็คเกจ java.time:
import java. time. LocalDate ;
import java. time. Month ;
public class Playground3 {
public static void main ( String [ ] args) {
LocalDate date = LocalDate . of ( 2022 , Month . APRIL, 4 ) ;
System . out. println ( "year = " + date. getYear ( ) ) ;
System . out. println ( "month = " + date. getMonth ( ) ) ;
System . out. println ( "DayOfMonth = " + date. getDayOfMonth ( ) ) ;
System . out. println ( "DayOfWeek = " + date. getDayOfWeek ( ) ) ;
System . out. println ( "isLeapYear = " + date. isLeapYear ( ) ) ;
}
}
ตัวอย่างการใช้คลาส LocalTime เพื่อคำนวณเวลา:
LocalTime time = LocalTime . of ( 20 , 30 ) ;
int hour = time. getHour ( ) ;
int minute = time. getMinute ( ) ;
time = time. withSecond ( 6 ) ;
time = time. plusMinutes ( 3 ) ;
การเพิ่มเขตเวลา:
ZoneId zone = ZoneId . of ( "Canada/Eastern" ) ;
LocalDate localDate = LocalDate . of ( 2022 , Month . APRIL, 4 ) ;
ZonedDateTime zonedDateTime = date. atStartOfDay ( zone) ;
7.NullPointerException นักพัฒนาทุกคนเกลียด NullPointerException อาจเป็นเรื่องยากเป็นพิเศษเมื่อ StackTrace ไม่ได้ให้ข้อมูลที่เป็นประโยชน์เกี่ยวกับปัญหาที่แท้จริง เพื่อสาธิตสิ่งนี้ มาดูโค้ดตัวอย่างกัน:
package com. bazlur ;
public class Main {
public static void main ( String [ ] args) {
User user = null ;
getLengthOfUsersName ( user) ;
}
public static void getLengthOfUsersName ( User user) {
System . out. println ( "Length of first name: " + user. getName ( ) . getFirstName ( ) ) ;
}
}
class User {
private Name name;
private String email;
public User ( Name name, String email) {
this . name = name;
this . email = email;
}
}
class Name {
private String firstName;
private String lastName;
public Name ( String firstName, String lastName) {
this . firstName = firstName;
this . lastName = lastName;
}
}
ดูวิธีการพื้นฐานในข้อนี้ เราเห็นว่า
NullPointerException จะถูกส่งต่อ ไป หากเรารันและคอมไพล์โค้ดในเวอร์ชันก่อน Java 14 เราจะได้รับ StackTrace ต่อไปนี้:
Exception in thread "main" java. lang. NullPointerException
at com. bazlur. Main. getLengthOfUsersName ( Main . java: 11 )
at com. bazlur. Main. main ( Main . java: 7 )
มีข้อมูลน้อยมากที่นี่เกี่ยวกับสถานที่และสาเหตุที่
NullPointerException เกิด ขึ้น แต่ใน Java 14 และเวอร์ชันใหม่กว่า เราได้รับข้อมูลเพิ่มเติมมากมายใน StackTrace ซึ่งสะดวกมาก ใน Java 14 เราจะเห็น:
Exception in thread "main" java. lang. NullPointerException: Cannot invoke "ca.bazlur.playground.User.getName()" because "user" is null
at ca. bazlur. playground. Main. getLengthOfUsersName ( Main . java: 12 )
at ca. bazlur. playground. Main. main ( Main . java: 8 )
8. อนาคตที่สมบูรณ์ เราเขียนโปรแกรมทีละบรรทัด และมักจะดำเนินการทีละบรรทัด แต่มีบางครั้งที่เราต้องการการดำเนินการแบบขนานเพื่อทำให้โปรแกรมเร็วขึ้น สำหรับสิ่งนี้เรามักจะใช้ Java Thread การเขียนโปรแกรมเธรด Java ไม่ได้เกี่ยวกับการเขียนโปรแกรมแบบขนานเสมอไป แต่มันทำให้เราสามารถเขียนโมดูลโปรแกรมอิสระหลายโมดูลที่จะดำเนินการอย่างอิสระและมักจะเป็นแบบอะซิงโครนัสด้วยซ้ำ อย่างไรก็ตาม การตั้งโปรแกรมเธรดนั้นค่อนข้างยาก โดยเฉพาะสำหรับผู้เริ่มต้น นี่คือสาเหตุที่ Java 8 เสนอ API ที่เรียบง่ายกว่าซึ่งช่วยให้คุณสามารถรันส่วนหนึ่งของโปรแกรมแบบอะซิงโครนัสได้ มาดูตัวอย่างกัน สมมติว่าเราจำเป็นต้องเรียก REST API สามรายการแล้วรวมผลลัพธ์เข้าด้วยกัน เราสามารถเรียกพวกมันทีละคนได้ หากแต่ละรายการใช้เวลาประมาณ 200 มิลลิวินาที เวลารวมในการรับก็จะใช้เวลาประมาณ 600 มิลลิวินาที จะเป็นอย่างไรถ้าเราสามารถรันมันคู่ขนานได้? เนื่องจากโปรเซสเซอร์สมัยใหม่มักเป็นแบบมัลติคอร์ จึงสามารถรองรับการเรียกพักสามครั้งบนโปรเซสเซอร์สามตัวที่แตกต่างกันได้อย่างง่ายดาย การใช้ CompletableFuture เราสามารถทำสิ่งนี้ได้อย่างง่ายดาย
package ca. bazlur. playground ;
import java. time. Duration ;
import java. time. Instant ;
import java. util. List ;
import java. util. concurrent. CompletableFuture ;
import java. util. concurrent. ExecutionException ;
import java. util. concurrent. TimeUnit ;
public class SocialMediaService {
public static void main ( String [ ] args) throws ExecutionException , InterruptedException {
var service = new SocialMediaService ( ) ;
var start = Instant . now ( ) ;
var posts = service. fetchAllPost ( ) . get ( ) ;
var duration = Duration . between ( start, Instant . now ( ) ) ;
System . out. println ( "Total time taken: " + duration. toMillis ( ) ) ;
}
public CompletableFuture > fetchAllPost ( ) {
var facebook = CompletableFuture . supplyAsync ( this :: fetchPostFromFacebook ) ;
var linkedIn = CompletableFuture . supplyAsync ( this :: fetchPostFromLinkedIn ) ;
var twitter = CompletableFuture . supplyAsync ( this :: fetchPostFromTwitter ) ;
var futures = List . of ( facebook, linkedIn, twitter) ;
return CompletableFuture . allOf ( futures. toArray ( futures. toArray ( new CompletableFuture [ 0 ] ) ) )
. thenApply ( future -> futures. stream ( )
. map ( CompletableFuture :: join )
. toList ( ) ) ;
}
private String fetchPostFromTwitter ( ) {
sleep ( 200 ) ;
return "Twitter" ;
}
private String fetchPostFromLinkedIn ( ) {
sleep ( 200 ) ;
return "LinkedIn" ;
}
private String fetchPostFromFacebook ( ) {
sleep ( 200 ) ;
return "Facebook" ;
}
private void sleep ( int millis) {
try {
TimeUnit . MILLISECONDS. sleep ( millis) ;
} catch ( InterruptedException e) {
throw new RuntimeException ( e) ;
}
}
}
9. นิพจน์แลมบ์ดา นิพจน์ Lambda อาจเป็นคุณลักษณะที่ทรงพลังที่สุดของภาษา Java พวกเขาเปลี่ยนวิธีการเขียนโค้ดของเรา นิพจน์แลมบ์ดาเปรียบเสมือนฟังก์ชันนิรนามที่สามารถรับอาร์กิวเมนต์และส่งกลับค่าได้ เราสามารถกำหนดฟังก์ชันให้กับตัวแปรและส่งผ่านเป็นอาร์กิวเมนต์ให้กับเมธอดได้ และเมธอดก็สามารถคืนค่าฟังก์ชันนั้นได้ เขามีร่างกาย ข้อแตกต่างเพียงอย่างเดียวจากวิธีนี้คือไม่มีชื่อ สำนวนที่สั้นและกระชับ โดยปกติแล้วจะไม่มีโค้ดสำเร็จรูปจำนวนมาก มาดูตัวอย่างที่เราจำเป็นต้องแสดงรายการไฟล์ทั้งหมดในไดเร็กทอรีที่มีนามสกุล .java
var directory = new File ( "./src/main/java/ca/bazlur/playground" ) ;
String [ ] list = directory. list ( new FilenameFilter ( ) {
@Override
public boolean accept ( File dir, String name) {
return name. endsWith ( ".java" ) ;
}
} ) ;
หากคุณดูโค้ดนี้อย่างใกล้ชิด เราได้ส่งผ่าน class
list() ที่ไม่ระบุชื่อภายในไปยังเมธอด แล้ว และในคลาสชั้นในเราวางตรรกะสำหรับการกรองไฟล์ โดยพื้นฐานแล้ว เราสนใจตรรกะส่วนนี้ ไม่ใช่รูปแบบรอบๆ ตรรกะ นิพจน์ lambda ช่วยให้เราสามารถลบเทมเพลตทั้งหมดได้ และเราสามารถเขียนโค้ดที่เราสนใจได้ นี่คือตัวอย่าง:
var directory = new File ( "./src/main/java/ca/bazlur/playground" ) ;
String [ ] list = directory. list ( ( dir, name) -> name. endsWith ( ".java" ) ) ;
แน่นอนว่านี่เป็นเพียงตัวอย่างเดียว นิพจน์ lambda มีประโยชน์อื่นๆ มากมาย
10. สตรีม API ในการทำงานประจำวันของเรา งานทั่วไปอย่างหนึ่งคือการประมวลผลชุดข้อมูล มีการดำเนินการทั่วไปหลายประการ เช่น การกรอง การแปลง และการรวบรวมผลลัพธ์ ก่อน Java 8 การดำเนินการดังกล่าวมีความจำเป็นโดยธรรมชาติ เราต้องเขียนโค้ดตามความตั้งใจของเรา (เช่น สิ่งที่เราต้องการทำให้สำเร็จ) และวิธีที่เราต้องการจะทำมัน ด้วยการประดิษฐ์นิพจน์แลมบ์ดาและ Stream API ทำให้ตอนนี้เราสามารถเขียนฟังก์ชันการประมวลผลข้อมูลอย่างชัดเจนได้ เราเพียงแสดงเจตนาของเราเท่านั้น และเราไม่จำเป็นต้องจดบันทึกว่าเราจะได้ผลอย่างไร นี่คือตัวอย่าง: เรามีรายชื่อหนังสือและเราต้องการค้นหาชื่อหนังสือ Java ทั้งหมด โดยคั่นด้วยเครื่องหมายจุลภาคและจัดเรียง
public static String getJavaBooks ( List books) {
return books. stream ( )
. filter ( book -> Objects . equals ( book. language ( ) , "Java" ) )
. sorted ( Comparator . comparing ( Book :: price ) )
. map ( Book :: name )
. collect ( Collectors . joining ( ", " ) ) ;
}
โค้ดด้านบนนั้นเรียบง่าย อ่านได้ และกระชับ แต่ด้านล่างคุณจะเห็นรหัสทางเลือกอื่น:
public static String getJavaBooksImperatively ( List books) {
var filteredBook = new ArrayList ( ) ;
for ( Book book : books) {
if ( Objects . equals ( book. language ( ) , "Java" ) ) {
filteredBook. add ( book) ;
}
}
filteredBook. sort ( new Comparator ( ) {
@Override
public int compare ( Book o1, Book o2) {
return Integer . compare ( o1. price ( ) , o2. price ( ) ) ;
}
} ) ;
var joiner = new StringJoiner ( "," ) ;
for ( Book book : filteredBook) {
joiner. add ( book. name ( ) ) ;
}
return joiner. toString ( ) ;
}
แม้ว่าทั้งสองวิธีจะส่งกลับค่าเดียวกัน แต่เราก็สามารถเห็นความแตกต่างได้อย่างชัดเจน
GO TO FULL VERSION