1. แนะนำอินเทอร์เฟซ
วันนี้เป็นวันของคุณสำหรับความรู้ หัวข้อใหม่และน่าสนใจอีกอย่างคืออินเทอร์เฟซ
แนวคิดของอินเทอร์เฟซคือลูกของหลักการนามธรรมและความหลากหลาย อินเทอร์เฟซคล้ายกับคลาสนามธรรมมาก ซึ่งเมธอดทั้งหมดเป็นแบบนามธรรม มีการประกาศในลักษณะเดียวกับ class แต่เราใช้interface
คีย์เวิร์ด
interface Feline
{
void purr();
void meow();
void growl();
}
ข้อเท็จจริงที่เป็นประโยชน์เกี่ยวกับอินเทอร์เฟซมีดังนี้
1. การประกาศอินเทอร์เฟซ
interface Drawable
{
void draw();
}
interface HasValue
{
int getValue();
}
- แทนที่จะเป็น
class
คีย์เวิร์ด เราเขียนinterface
. - มันมีวิธีการที่เป็นนามธรรมเท่านั้น (อย่าเขียน
abstract
คำหลัก) - ในความเป็นจริงอินเทอร์เฟซมี
public
วิธีการ ทั้งหมด
อินเทอร์เฟซสามารถสืบทอดอินเทอร์เฟซเท่านั้น แต่อินเทอร์เฟซสามารถมีผู้ปกครองได้หลายคน อีกวิธีในการพูดคือการบอกว่า Java มีการสืบทอดอินเทอร์เฟซหลายรายการ ตัวอย่าง:
interface Piece extends Drawable, HasValue
{
int getX();
int getY();
}
3. การสืบทอดคลาสจากอินเตอร์เฟส
คลาสสามารถสืบทอดหลายอินเตอร์เฟส (จากคลาสเดียวเท่านั้น) สิ่งนี้ทำได้โดยใช้implements
คำหลัก ตัวอย่าง:
abstract class ChessItem implements Drawable, HasValue
{
private int x, y, value;
public int getValue()
{
return value;
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
}
คลาส ChessItem ถูกประกาศเป็นนามธรรม: มันนำเมธอดที่สืบทอดมาทั้งหมดมาใช้ ยกเว้นdraw
. กล่าวอีกนัยหนึ่งChessItem
คลาสมีหนึ่งเมธอดนามธรรมdraw()
—
ความหมายทางเทคนิคของ คำหลัก extends
and implements
เหมือนกัน: ทั้งคู่เป็นกรรมพันธุ์ ความแตกต่างถูกสร้างขึ้นเพื่อปรับปรุงการอ่านรหัส เรายังบอกว่าคลาสได้รับการสืบทอด (ผ่านextends
) และอินเทอร์เฟซถูกนำไปใช้ (ผ่านimplements
)
4. ตัวแปร
นี่คือสิ่งที่สำคัญที่สุด: ไม่สามารถประกาศตัวแปรธรรมดาในอินเทอร์เฟซได้ (แม้ว่าตัวแปรแบบสแตติกจะทำได้ก็ตาม)
แต่ทำไมเราถึงต้องการอินเทอร์เฟซ? ใช้เมื่อไหร่? อินเทอร์เฟซมีข้อดีสองประการที่เหนือกว่าคลาส:
2. แยก "คำอธิบายวิธีการ" ออกจากการนำไปใช้
ก่อนหน้านี้ เราเคยกล่าวไว้ว่าหากคุณต้องการอนุญาตให้เมธอดของคลาสของคุณถูกเรียกจากคลาสอื่น วิธีการของคุณจะต้องถูกทำเครื่องหมายด้วยpublic
คีย์เวิร์ด หากคุณต้องการให้เมธอดบางส่วนถูกเรียกจากในคลาสของคุณเท่านั้น คุณต้องทำเครื่องหมายด้วยprivate
คีย์เวิร์ด กล่าวอีกนัยหนึ่ง เราแบ่งวิธีการของชั้นเรียนออกเป็นสองประเภท: "สำหรับทุกคนใช้" และ "สำหรับใช้เองเท่านั้น"
อินเทอร์เฟซช่วยเสริมความแข็งแกร่งของแผนกนี้ให้แข็งแกร่งยิ่งขึ้น เราจะสร้าง "คลาสพิเศษสำหรับทุกคนที่ใช้" รวมถึงคลาสที่สอง "สำหรับใช้เองเท่านั้น" ซึ่งจะสืบทอดคลาสแรก นี่คือลักษณะคร่าวๆ:
ก่อน | หลังจาก |
---|---|
|
|
|
|
เราแบ่งคลาสของเราออกเป็นสอง: อินเทอร์เฟซและคลาสที่สืบทอดอินเทอร์เฟซ และข้อดีที่นี่คืออะไร?
คลาสที่แตกต่างกันจำนวนมากสามารถใช้ (สืบทอด) อินเทอร์เฟซเดียวกันได้ และแต่ละคนสามารถมีพฤติกรรมของตนเองได้ ตัวอย่างเช่น มีการใช้งาน อินเทอร์เฟซArrayList
LinkedList
ที่แตกต่างกันสองแบบList
ดังนั้นเราจึงซ่อนไม่เพียง แต่การใช้งานที่หลากหลาย แต่ยังรวมถึงคลาสการใช้งานด้วย (เนื่องจากเราต้องการส่วนต่อประสานในรหัสเท่านั้น) สิ่งนี้ทำให้เรามีความยืดหยุ่นมาก: ทันทีที่โปรแกรมทำงาน เราสามารถแทนที่วัตถุหนึ่งด้วยอีกวัตถุหนึ่ง เปลี่ยนพฤติกรรมของวัตถุโดยไม่ส่งผลกระทบต่อคลาสทั้งหมดที่ใช้งาน
นี่เป็นเทคนิคที่ทรงพลังมากเมื่อรวมกับความหลากหลาย สำหรับตอนนี้ มันยังห่างไกลจากความชัดเจนว่าเหตุใดคุณจึงควรทำเช่นนี้ ก่อนอื่นคุณต้องพบกับโปรแกรมที่มีคลาสหลายสิบหรือหลายร้อยคลาสเพื่อที่จะเข้าใจว่าอินเทอร์เฟซสามารถทำให้ชีวิตของคุณง่ายขึ้นกว่าที่ไม่มีพวกมัน
3. มรดกหลายรายการ
ใน Java คลาสทั้งหมดสามารถมีคลาสพาเรนต์ได้เพียงหนึ่งคลาสเท่านั้น ในภาษาโปรแกรมอื่นๆ คลาสมักจะมีพาเรนต์หลายคลาส สะดวกมาก แต่ก็ทำให้เกิดปัญหามากมาย
ผู้สร้างของ Java มาถึงจุดประนีประนอม: พวกเขาห้ามการสืบทอดหลายคลาส แต่อนุญาตให้มีการสืบทอดอินเทอร์เฟซหลายรายการ อินเทอร์เฟซสามารถมีหลายอินเทอร์เฟซหลัก คลาสสามารถมีอินเทอร์เฟซพาเรนต์ได้หลายตัว แต่คลาสพาเรนต์เดียวเท่านั้น
เหตุใดพวกเขาจึงห้ามการสืบทอดหลายคลาส แต่อนุญาตให้มีการสืบทอดอินเทอร์เฟซหลายรายการ เพราะปัญหามรดกที่เรียกว่าเพชร:

เมื่อคลาส B สืบทอดคลาส A ก็จะไม่รู้อะไรเลยเกี่ยวกับคลาส C และ D ดังนั้นจึงใช้ตัวแปรของคลาส A ตามที่เห็นสมควร คลาส C ทำเช่นเดียวกัน: ใช้ตัวแปรของคลาส A แต่ด้วยวิธีที่แตกต่างกัน และทั้งหมดนี้ส่งผลให้เกิดความขัดแย้งในคลาส D
ลองดูตัวอย่างง่ายๆ ต่อไปนี้ สมมติว่าเรามี 3 คลาส:
class Data
{
protected int value;
}
class XCoordinate extends Data
{
public void setX (int x) { value = x;}
public int getX () { return value;}
}
class YCoordinate extends Data
{
public void setY (int y) { value = y;}
public int getY () { return value; }
}
ชั้นข้อมูลเก็บvalue
ตัวแปร คลาสที่สืบทอดมาของ XCoordinate ใช้ตัวแปรนั้นเพื่อเก็บx
ค่า และYCoordinate
คลาสที่สืบทอดมาจะใช้ตัวแปรนั้นเพื่อเก็บy
ค่า
และมันใช้งานได้ แยกกัน แต่ถ้าเราต้องการให้คลาส XYCoordinates สืบทอดทั้งคลาสXCoordinate
และYCoordinate
คลาส เราจะได้รับรหัสที่เสียหาย คลาสนี้จะมีเมธอดของคลาสบรรพบุรุษ แต่จะใช้งานไม่ถูกต้องเพราะมีเหมือนกันvalue variable
.
แต่เนื่องจากอินเทอร์เฟซไม่สามารถมีตัวแปรได้ จึงไม่มีความขัดแย้งแบบนี้ ดังนั้น จึงอนุญาตให้มีการสืบทอดอินเทอร์เฟซหลายรายการได้
GO TO FULL VERSION