สวัสดี! วันนี้เราจะพูดถึงแนวคิดที่สำคัญใน Java - อินเทอร์เฟซ คำนี้คงจะคุ้นเคยสำหรับคุณ ตัวอย่างเช่น โปรแกรมคอมพิวเตอร์และเกมส่วนใหญ่มีอินเทอร์เฟซ ในความหมายกว้างๆ อินเทอร์เฟซคือ "รีโมทคอนโทรล" ประเภทหนึ่งที่เชื่อมโยงทั้งสองฝ่ายที่มีปฏิสัมพันธ์ระหว่างกัน ตัวอย่างง่ายๆ ของอินเทอร์เฟซจากชีวิตประจำวันคือรีโมทคอนโทรลของทีวี โดยจะเชื่อมต่อวัตถุสองชิ้น บุคคลกับทีวี และทำงานที่แตกต่างกัน: เพิ่มหรือลดระดับเสียง เปลี่ยนช่อง เปิดหรือปิดทีวี ด้านหนึ่ง (บุคคล) จำเป็นต้องเข้าถึงอินเทอร์เฟซ (กดปุ่มรีโมทคอนโทรล) เพื่อให้อีกด้านหนึ่งดำเนินการ เช่นให้ทีวีเปลี่ยนช่องไปช่องถัดไป ในกรณีนี้ผู้ใช้ไม่จำเป็นต้องทราบอุปกรณ์ของทีวีและวิธีการเปลี่ยนช่องภายใน
ผู้ใช้ทุกคนที่มีสิทธิ์เข้าถึงคืออินเทอร์เฟซ ภารกิจหลักคือการได้รับผลลัพธ์ที่ต้องการ สิ่งนี้เกี่ยวข้องกับการเขียนโปรแกรมและ Java อย่างไร? โดยตรง :) การสร้างอินเทอร์เฟซนั้นคล้ายกับการสร้างคลาสปกติมาก แต่แทนที่จะเป็นคำที่
คุณทำงานกับอินเทอร์เฟซ

class
เราระบุคำว่าinterface
. มาดูอินเทอร์เฟซ Java ที่ง่ายที่สุดแล้วดูว่ามันทำงานอย่างไรและจำเป็นสำหรับอะไร:
public interface Swimmable {
public void swim();
}
เราสร้างอินเทอร์เฟซSwimmable
ที่สามารถว่ายน้ำได้ นี่คือสิ่งที่คล้ายกับรีโมตคอนโทรลของเราซึ่งมี "ปุ่ม" เพียงปุ่มเดียว: วิธีการswim()
คือ "ว่ายน้ำ" เราจะใช้ “ รีโมทคอนโทรล ” นี้ได้อย่างไร? เพื่อจุดประสงค์นี้วิธีการคือ จำเป็นต้องใช้ปุ่มบนรีโมทคอนโทรลของเรา หากต้องการใช้อินเทอร์เฟซ วิธีการของมันจะต้องได้รับการปรับใช้โดยคลาสบางคลาสของโปรแกรมของเรา มาดูชั้นเรียนที่มีวัตถุตรงกับคำอธิบายว่า "ว่ายน้ำได้" ตัวอย่างเช่น คลาสเป็ดมีความเหมาะสมDuck
:
public class Duck implements Swimmable {
public void swim() {
System.out.println("Duck, swim!");
}
public static void main(String[] args) {
Duck duck = new Duck();
duck.swim();
}
}
เราเห็นอะไรที่นี่? คลาสDuck
เชื่อมโยงกับอินเทอร์เฟซโดยSwimmable
ใช้คีย์เวิร์ด implements
หากคุณจำได้ เราใช้กลไกที่คล้ายกันเพื่อเชื่อมโยงสองคลาสในการสืบทอด มีเพียงคำว่า " ขยาย " เท่านั้น “ public class Duck implements Swimmable
” สามารถแปลตามตัวอักษรเพื่อความชัดเจน: “คลาสสาธารณะDuck
ใช้อินเทอร์เฟซSwimmable
” ซึ่งหมายความว่าคลาสที่เกี่ยวข้องกับอินเทอร์เฟซจะต้องใช้วิธีการทั้งหมดของมัน โปรดทราบ:ในชั้นเรียนของเรามี method อยู่Duck
เช่นเดียวกับใน interface และภายในนั้นมีตรรกะบางอย่าง นี่เป็นข้อกำหนดบังคับ หากเราเพิ่งเขียน “ ” และไม่ได้สร้างเมธอดในคลาสคอมไพเลอร์ก็จะแจ้งข้อผิดพลาดให้เราทราบ: Duck ไม่ใช่นามธรรมและไม่ได้แทนที่เมธอดนามธรรม swim() ใน Swimmable ทำไมสิ่งนี้ถึงเกิดขึ้น? หากเราอธิบายข้อผิดพลาดโดยใช้ตัวอย่างทีวีปรากฎว่าเราให้รีโมทคอนโทรลแก่บุคคลด้วยปุ่ม "เปลี่ยนช่อง" จากทีวีที่ไม่รู้วิธีเปลี่ยนช่อง เมื่อถึงจุดนี้ให้กดปุ่มเท่าที่คุณต้องการก็จะไม่มีอะไรทำงาน รีโมทคอนโทรลเองไม่เปลี่ยนช่อง: เพียงส่งสัญญาณไปยังทีวีซึ่งภายในนั้นมีกระบวนการที่ซับซ้อนในการเปลี่ยนช่อง เช่นเดียวกับเป็ดของเรา: มันจะต้องว่ายน้ำได้จึงจะสามารถเข้าถึงได้โดยใช้อินเทอร์เฟซ. หากเธอไม่ทราบวิธีการทำเช่นนี้ อินเทอร์เฟซจะไม่เชื่อมต่อทั้งสองด้าน - บุคคลและโปรแกรม บุคคลจะไม่สามารถใช้วิธีการเพื่อทำให้วัตถุภายในโปรแกรมลอยได้ ตอนนี้คุณได้เห็นชัดเจนยิ่งขึ้นว่าอินเทอร์เฟซมีไว้เพื่ออะไร อินเทอร์เฟซอธิบายลักษณะการทำงานที่คลาสที่ใช้อินเทอร์เฟซนั้นต้องมี “พฤติกรรม” คือชุดของวิธีการ หากเราต้องการสร้างผู้ส่งสารหลายราย วิธีที่ง่ายที่สุดในการทำเช่นนี้คือการสร้างอินเทอร์เฟซ ผู้ส่งสารคนใดสามารถทำอะไรได้บ้าง? รับและส่งข้อความในรูปแบบที่เรียบง่าย Swimmable
swim()
public class Duck implements Swimmable
swim()
Duck
Swimmable
Swimmable
swim()
Duck
Messenger
public interface Messenger{
public void sendMessage();
public void getMessage();
}
และตอนนี้เราสามารถสร้างคลาส Messenger ของเราได้โดยใช้อินเทอร์เฟซนี้ คอมไพเลอร์จะ "บังคับ" ให้เรานำไปใช้ภายในคลาส โทรเลข:
public class Telegram implements Messenger {
public void sendMessage() {
System.out.println("Sending a message to Telegram!");
}
public void getMessage() {
System.out.println("Reading the message in Telegram!");
}
}
วอทส์แอพพ์:
public class WhatsApp implements Messenger {
public void sendMessage() {
System.out.println("Sending a WhatsApp message!");
}
public void getMessage() {
System.out.println("Reading a WhatsApp message!");
}
}
ไวเบอร์:
public class Viber implements Messenger {
public void sendMessage() {
System.out.println("Sending a message to Viber!");
}
public void getMessage() {
System.out.println("Reading a message in Viber!");
}
}
สิ่งนี้ให้ประโยชน์อะไรบ้าง? สิ่งสำคัญที่สุดคือการมีเพศสัมพันธ์แบบหลวม ลองจินตนาการว่าเรากำลังออกแบบโปรแกรมที่เราจะรวบรวมข้อมูลลูกค้า ชั้นเรียนClient
จะต้องมีช่องที่ระบุว่า Messenger ตัวใดที่ไคลเอ็นต์ใช้ หากไม่มีอินเทอร์เฟซก็จะดูแปลก:
public class Client {
private WhatsApp whatsApp;
private Telegram telegram;
private Viber viber;
}
เราสร้างช่องข้อมูลไว้ 3 ช่อง แต่ลูกค้าสามารถมี Messenger เพียงช่องเดียวได้อย่างง่ายดาย เราแค่ไม่รู้ว่าอันไหน และเพื่อไม่ให้ถูกทิ้งไว้โดยไม่มีการสื่อสารกับลูกค้า คุณต้อง "ผลักดัน" ตัวเลือกที่เป็นไปได้ทั้งหมดเข้ามาในชั้นเรียน ปรากฎว่ามีหนึ่งหรือสองคนอยู่ที่นั่นเสมอnull
และไม่จำเป็นเลยเพื่อให้โปรแกรมทำงานได้ ควรใช้อินเทอร์เฟซของเราแทน:
public class Client {
private Messenger messenger;
}
นี่คือตัวอย่างของ “ข้อต่อหลวม”! แทนที่จะระบุคลาส Messenger เฉพาะใน class Client
เราเพียงแต่ระบุว่าไคลเอ็นต์มี Messenger อันไหนจะถูกกำหนดในระหว่างหลักสูตร แต่ทำไมเราถึงต้องการอินเทอร์เฟซสำหรับสิ่งนี้? ทำไมพวกเขาถึงเพิ่มเข้าไปในภาษาเลย? คำถามดีและถูกต้อง! ผลลัพธ์เดียวกันนี้สามารถทำได้โดยใช้มรดกธรรมดาใช่ไหม? คลาสMessenger
คือคลาสพาเรนต์ และและViber
คือทายาท แท้จริงแล้วมันเป็นไปได้ที่จะทำเช่นนั้น แต่มีสิ่งหนึ่งที่จับได้ ดังที่คุณทราบแล้วว่า Java ไม่มีการสืบทอดหลายรายการ แต่มีการใช้งานอินเทอร์เฟซหลายอย่าง คลาสสามารถใช้อินเทอร์เฟซได้มากเท่าที่ต้องการ ลองนึกภาพว่าเรามีคลาสที่มีฟิลด์- แอปพลิเคชันที่ติดตั้งบนสมาร์ทโฟน Telegram
WhatsApp
Smartphone
Application
public class Smartphone {
private Application application;
}
แน่นอนว่าแอปพลิเคชั่นและผู้ส่งสารนั้นคล้ายกัน แต่ก็ยังเป็นสิ่งที่แตกต่างกัน Messenger เป็นได้ทั้งมือถือและเดสก์ท็อป ในขณะที่ Application เป็นแอปพลิเคชั่นมือถือ ดังนั้นหากเราใช้ inheritance เราจะไม่สามารถเพิ่ม object Telegram
ให้กับ class Smartphone
ได้ ท้ายที่สุดแล้ว คลาสTelegram
ไม่สามารถสืบทอดจากApplication
และจากMessenger
! และเราได้จัดการสืบทอดมันมาจากMessenger
และเพิ่มลงในคลาสในรูปแบบClient
นี้ แต่คลาสTelegram
สามารถใช้ทั้งสองอินเทอร์เฟซได้อย่างง่ายดาย! ดังนั้นในคลาส เราจึง สามารถClient
Implement วัตถุTelegram
เป็นMessenger
และในคลาสSmartphone
เป็น Application
ต่อไปนี้เป็นวิธีดำเนินการ:
public class Telegram implements Application, Messenger {
//...methods
}
public class Client {
private Messenger messenger;
public Client() {
this.messenger = new Telegram();
}
}
public class Smartphone {
private Application application;
public Smartphone() {
this.application = new Telegram();
}
}
ตอนนี้เราสามารถใช้คลาสTelegram
ได้ตามต้องการ ที่ไหนสักแห่งเขาจะแสดงในบทบาทของApplication
ที่ไหนสักแห่งในบทบาทMessenger
ของ คุณอาจสังเกตเห็นแล้วว่าวิธีการในอินเทอร์เฟซนั้น "ว่างเปล่า" เสมอนั่นคือไม่มีการใช้งาน เหตุผลง่ายๆ ก็คือ อินเทอร์เฟซอธิบายพฤติกรรม ไม่ใช่นำไปใช้ “ออบเจ็กต์ทั้งหมดของคลาสที่ใช้อินเทอร์เฟซSwimmable
จะต้องสามารถลอยได้” นั่นคือทั้งหมดที่อินเทอร์เฟซบอกเรา ปลา เป็ด หรือม้าจะว่าย น้ำได้อย่างไร มีคำถามสำหรับคลาสFish
และไม่ใช่สำหรับอินเทอร์เฟซ เช่นเดียวกับการเปลี่ยนช่องเป็นหน้าที่ของทีวี รีโมทเพียงกดปุ่มให้คุณทำ อย่างไรก็ตาม Java8 มีการเพิ่มเติมที่น่าสนใจ - วิธีการเริ่มต้น ตัวอย่างเช่น อินเทอร์เฟซของคุณมี 10 วิธี มี 9 รายการที่มีการนำไปใช้ที่แตกต่างกันในคลาสที่แตกต่างกัน แต่มีรายการหนึ่งถูกนำไปใช้เหมือนกันในทุกคลาส ก่อนหน้านี้ก่อนการเปิดตัว Java8 วิธีการภายในอินเทอร์เฟซไม่มีการนำไปใช้เลย: คอมไพเลอร์แสดงข้อผิดพลาดทันที ตอนนี้คุณสามารถทำเช่นนี้: Duck
Horse
public interface Swimmable {
public default void swim() {
System.out.println("Swim!");
}
public void eat();
public void run();
}
การใช้คำหลักdefault
เราได้สร้างวิธีการในส่วนต่อประสานที่มีการใช้งานเริ่มต้น เราจะต้องนำ อีกสองวิธี ไปใช้ eat()
และrun()
ตัวเราเองในทุกคลาสที่จะนำไปSwimmable
ใช้ ไม่จำเป็นต้องทำเช่นนี้ กับวิธีการswim()
: การใช้งานจะเหมือนกันในทุกคลาส อย่างไรก็ตาม คุณได้พบอินเทอร์เฟซมากกว่าหนึ่งครั้งในงานที่ผ่านมา แม้ว่าคุณจะไม่ได้สังเกตด้วยตัวเองก็ตาม :) นี่คือตัวอย่างที่ชัดเจน: 
List
และSet
! แม่นยำยิ่งขึ้นด้วยการใช้งาน - ArrayList
, LinkedList
และHashSet
อื่น ๆ แผนภาพเดียวกันนี้แสดงตัวอย่างเมื่อคลาสหนึ่งใช้งานหลายอินเทอร์เฟซพร้อมกัน ตัวอย่างเช่นLinkedList
ใช้อินเทอร์เฟซList
และDeque
(คิวสองด้าน) คุณยังคุ้นเคยกับอินเทอร์เฟซMap
หรือมากกว่านั้นด้วยการใช้งานHashMap
- อย่างไรก็ตามในแผนภาพนี้คุณสามารถเห็นคุณลักษณะหนึ่ง: อินเทอร์เฟซสามารถสืบทอดจากกันและกันได้ อินเทอร์เฟซSortedMap
สืบทอดมาจากMap
และDeque
สืบทอดมาจากQueue
คิว นี่เป็นสิ่งจำเป็นถ้าคุณต้องการแสดงการเชื่อมต่อระหว่างอินเทอร์เฟซ แต่อินเทอร์เฟซหนึ่งเป็นเวอร์ชันขยายของอีกอินเทอร์เฟซหนึ่ง ลองดูตัวอย่างด้วยอินเทอร์เฟซQueue
- คิว เรายังไม่ได้ดูคอลเลกชันต่างๆQueue
แต่ค่อนข้างเรียบง่ายและจัดเรียงเหมือนแถวปกติในร้านค้า คุณสามารถเพิ่มองค์ประกอบที่ส่วนท้ายของคิวเท่านั้น และนำองค์ประกอบเหล่านั้นออกจากจุดเริ่มต้นเท่านั้น ในขั้นตอนหนึ่ง นักพัฒนาจำเป็นต้องมีเวอร์ชันขยายของคิวเพื่อให้สามารถเพิ่มและรับองค์ประกอบจากทั้งสองฝ่ายได้ นี่คือวิธีการสร้างอินเทอร์เฟซDeque
- คิวแบบสองทาง ประกอบด้วยวิธีการทั้งหมดของคิวปกติ เนื่องจากเป็น "พาเรนต์" ของคิวแบบสองทาง แต่มีการเพิ่มวิธีการใหม่แล้ว
GO TO FULL VERSION