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

public class Month {

   private String name;
   private int daysCount;

   public Month(String name, int daysCount) {
       this.name = name;
       this.daysCount = daysCount;
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getDaysCount() {
       return daysCount;
   }

   public void setDaysCount(int daysCount) {
       this.daysCount = daysCount;
   }

   @Override
   public String toString() {
       return "Month{" +
               "name='" + name + '\'' +
               ", daysCount=" + daysCount +
               '}';
   }
}
พังทั้งคัน! เรามีMonthคลาส, ฟิลด์บังคับ, getter/setters toString()และ แน่นอนว่าเราต้องเพิ่มequals()และhashCode()เพื่อบรรลุความสุขสมบูรณ์ :) แต่ที่นี่เรามีปัญหาทางความคิด อย่างที่คุณคงจำได้ ข้อดีหลักประการหนึ่งของ OOP คือทำให้ง่ายต่อการจำลองเอนทิตีจากโลกแห่งความเป็นจริง เก้าอี้ รถยนต์ ดาวเคราะห์ — แนวคิดทั้งหมดเหล่านี้จากชีวิตธรรมดาสามารถแสดงได้อย่างง่ายดายในโปรแกรมด้วยความช่วยเหลือจากสิ่งที่เป็นนามธรรม ปัญหาคือเอนทิตีในโลกแห่งความเป็นจริงบางตัวมีช่วงของค่าที่จำกัดอย่างเคร่งครัด ปีหนึ่งมีแค่ 4 ฤดู มีเพียง 8 โน้ตในอ็อกเทฟ ปฏิทินมีแค่ 12 เดือน และ Danny Ocean of Ocean's 11 มีเพื่อนเพียง 11 คน (แม้ว่าจะไม่สำคัญก็ตาม :)) สิ่งที่สำคัญคือคลาส Java ธรรมดาไม่สามารถจำลองเอนทิตีเหล่านี้และบังคับใช้ข้อจำกัดตามธรรมชาติของพวกมันได้ ของเราMonthชั้นเรียนมีฟิลด์บังคับทั้งหมด แต่ถ้าโปรแกรมเมอร์คนอื่นใช้ ก็จะไม่มีใครหยุดเขาหรือเธอจากการสร้างอ็อบเจกต์ที่บ้าคลั่งได้:

public class Main {

   Month month1 = new Month("lolkek", 322);
   Month month2 = new Month("yahoooooooooooo", 12345);

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

public class Month {

   private String name;
   private int daysCount;

   private Month(String name, int daysCount) {
       this.name = name;
       this.daysCount = daysCount;
   }

   public static Month JANUARY = new Month("January", 31);
   public static Month FEBRUARY = new Month("February", 28);
   public static Month MARCH = new Month("March", 31);

   @Override
   public String toString() {
       return "Month{" +
               "name='" + name + '\'' +
               ", daysCount=" + daysCount +
               '}';
   }
}
เราได้ตัดจำนวนเดือนจากสิบสองเหลือสามเดือนเพื่อให้ตัวอย่างสั้นลง การออกแบบดังกล่าวทำให้สามารถแก้ปัญหาได้ ความสามารถในการสร้างอ็อบเจกต์ถูกจำกัดไว้เฉพาะคอนสตรัคเตอร์ส่วนตัว:

private Month(String name, int daysCount) {
       this.name = name;
       this.daysCount = daysCount;
   }
โปรแกรมเมอร์ที่ใช้คลาสไม่สามารถสร้างMonthวัตถุ ได้ พวกเขาต้องใช้อ็อบเจกต์สแตติกขั้นสุดท้ายที่ผู้พัฒนาคลาสจัดเตรียมไว้ให้ ตัวอย่างเช่น:

public class Main {

   public static void main(String[] args) {

       Month january = Month.JANUARY;
       System.out.println(january);
   }

}
แต่นักพัฒนา Java ให้ความสนใจกับปัญหาที่มีอยู่ แน่นอนว่าเป็นเรื่องดีที่โปรแกรมเมอร์สามารถคิดวิธีแก้ปัญหาโดยใช้เครื่องมือที่มีอยู่ในภาษาได้ แต่ดูไม่ง่ายเลย! จำเป็นต้องมีวิธีแก้ปัญหาที่ชัดเจนแม้กระทั่งสำหรับผู้เริ่มต้น และEnumปรากฏใน Java โดยทั่วไปEnumเป็นคลาส Java ที่ให้ชุดของค่าวัตถุที่จำกัด นี่คือลักษณะ:

public enum Month {
  
   JANUARY,
   FEBRUARY,
   MARCH
}
ในนิยาม เราระบุว่าEnumเป็นคลาส Java แต่จริงหรือไม่? ได้ และเรายังสามารถตรวจสอบได้ ตัวอย่างเช่น ลองทำให้Monthenum ของเราสืบทอดคลาสอื่น:

public abstract class AbstractMonth {
}

// Error! The extends clause cannot be used with an enum
public enum Month extends AbstractMonth {

   JANUARY,
   FEBRUARY,
   MARCH
}
ทำไมถึงเกิดขึ้น? เมื่อเราเขียน:

public enum Month
คอมไพเลอร์แปลงคำสั่งนี้เป็นรหัสต่อไปนี้:

public Class Month extends Enum
ดังที่คุณทราบแล้ว Java ไม่สนับสนุนการสืบทอดหลายรายการ ดังนั้นเราจึงรับมรดกไม่AbstractMonthได้ โครงสร้างใหม่นี้จะEnumนำไปใช้ได้อย่างไร? แล้วมันแตกต่างจากโครงสร้างแบบเก่าที่มีstatic finalฟิลด์อย่างไร? ตัวอย่างเช่น โครงสร้างแบบเก่าไม่อนุญาตให้เราใช้ชุดค่าของเราเองในswitchคำสั่ง ลองนึกภาพว่าเราต้องการสร้างโปรแกรมที่จะเตือนเราถึงวันหยุดที่มีการเฉลิมฉลองในแต่ละเดือน:

public class HolidayReminder {

   public void printHolidays(Month month) {

       switch (month) {

           // Error!
           case JANUARY:
       }
   }
}
อย่างที่คุณเห็น คอมไพเลอร์แสดงข้อผิดพลาดที่นี่ แต่เมื่อenumปรากฏใน Java 1.5 ทุกอย่างก็ง่ายขึ้นมาก:

public enum Month {

   JANUARY,
   FEBRUARY,
   MARCH
}

public class HolidayReminder {

   public void printHolidays(Month month) {

       switch (month) {
          
           case JANUARY:
               System.out.println("New Year's Day is January 1st!");
               break;
           case FEBRUARY:
               System.out.println("Valentine's Day is February 14th!");
               break;
           case MARCH:
               System.out.println("Saint Patrick's Day is March 17th!");
               break;
       }
   }
}


public class Main {

   public static void main(String[] args) {

       HolidayReminder reminder = new HolidayReminder();
       reminder.printHolidays(Month.JANUARY);

   }

}
เอาต์พุตคอนโซล:

New Year's Day is January 1st!
โปรดทราบว่าการเข้าถึงEnumออบเจกต์ยังคงเป็นแบบคงที่เหมือนก่อน Java 1.5 เราไม่จำเป็นต้องสร้างMonthวัตถุเพื่อเข้าถึงเดือน เมื่อทำงานกับ enums สิ่งสำคัญคือต้องไม่ลืมว่าEnumเป็นคลาสเต็มรูปแบบ ซึ่งหมายความว่า หากจำเป็น คุณสามารถกำหนดคอนสตรัคเตอร์และวิธีการในนั้นได้ ตัวอย่างเช่น ในโค้ดแฟรกเมนต์ก่อนหน้านี้ เราเพียงแค่ระบุค่า: JANUARY, FEBRUARY, MARCH อย่างไรก็ตาม เราสามารถขยายMonthenum ได้ดังนี้:

public enum Month {

   JANUARY("January", 31),
   FEBRUARY("February", 28),
   MARCH("March", 31),
   APRIL("April", 30),
   MAY("May", 31),
   JUNE("June", 30),
   JULY("July", 31),
   AUGUST("August", 31),
   SEPTEMBER("September", 30),
   OCTOBER("October", 31),
   NOVEMBER("November", 30),
   DECEMBER("December", 31);

   private String name;
   private int daysCount;

   Month(String name, int daysCount) {
       this.name = name;
       this.daysCount = daysCount;
   }

   public static Month[] getWinterMonths() {

       return new Month[]{DECEMBER, JANUARY, FEBRUARY};
   }

   public static Month[] getSummerMonths() {

       return new Month[]{JUNE, JULY, AUGUST};
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getDaysCount() {
       return daysCount;
   }

   public void setDaysCount(int daysCount) {
       this.daysCount = daysCount;
   }

   @Override
   public String toString() {
       return "Month{" +
               "name='" + name + '\'' +
               ", daysCount=" + daysCount +
               '}';
   }
}
ที่นี่เราให้enum2 ฟิลด์ของเรา (ชื่อเดือนและจำนวนวัน) ตัวสร้างที่ใช้ฟิลด์เหล่านี้ getter/setters เมธอดtoString()และ 2 เมธอดสแตติก อย่างที่คุณเห็นไม่มีปัญหากับสิ่งนี้ อีกครั้งEnumเป็นคลาสที่เต็มเปี่ยม:

import java.util.Arrays;

public class Main {

   public static void main(String[] args) {

       System.out.println(Arrays.toString(Month.getSummerMonths()));

   }

}
เอาต์พุตคอนโซล:

[Month{name='June', daysCount=30}, Month{name='July', daysCount=31}, Month{name='August', daysCount=31}]
สุดท้ายนี้ ผมอยากแนะนำหนังสือ Java ที่มีประโยชน์อย่างยิ่ง นั่นคือ" Effective Java" โดย Joshua Bloch เอนัม  ตัวอย่างการปฏิบัติ  การเพิ่มตัวสร้างและวิธีการ - 3ผู้เขียนเป็นหนึ่งในผู้สร้างของ Java ดังนั้นคุณสามารถไว้วางใจคำแนะนำของเขาเกี่ยวกับวิธีการใช้เครื่องมือภาษาอย่างถูกต้องและเหมาะสมได้อย่างแน่นอน :) สำหรับบทเรียนของเรา ฉันขอแนะนำให้คุณให้ความสนใจเป็นพิเศษกับบทของหนังสือเกี่ยวEnumกับ มีความสุขในการอ่าน! :)