การนำ Singleton ไปใช้กับ Enum (ใน Java)


178

ฉันได้อ่านว่ามันเป็นไปได้ที่จะนำไปใช้Singletonใน Java โดยใช้Enumเช่น:

public enum MySingleton {
     INSTANCE;   
}

แต่ข้างต้นทำงานอย่างไร โดยเฉพาะObjectจะต้องมีการยกตัวอย่าง ที่นี่วิธีMySingletonการยกตัวอย่าง? ใครกำลังทำnew MySingleton()อะไร


24
ใครกำลังทำ MySingleton ใหม่ () JVM คือใคร
Sotirios Delimanolis

37
INSTANCEpublic static final MySingleton INSTANCE = new MySingleton();เป็นเช่นเดียวกับ
Pshemo

6
ENUM - รับประกันซิงเกิล
ไม่ใช่ข้อผิดพลาด

อ่านdzone.com/articles/java-singletons-using-enum ทำไมต้องใช้ enum และไม่ใช่วิธีอื่น สั้น: ปัญหาเริ่มต้นเมื่อใช้การเป็นอันดับและการสะท้อนกลับ
Miyagi

คำตอบ:


203

นี้,

public enum MySingleton {
  INSTANCE;   
}

มี Constructor เปล่าโดยนัย ทำให้ชัดเจนแทน

public enum MySingleton {
    INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}

ถ้าคุณเพิ่มคลาสอื่นด้วยmain()เมธอดเช่น

public static void main(String[] args) {
    System.out.println(MySingleton.INSTANCE);
}

คุณจะเห็น

Here
INSTANCE

enumเขตข้อมูลเป็นค่าคงที่เวลารวบรวม แต่พวกเขาเป็นอินสแตนซ์ของenumประเภทของพวกเขา และพวกเขากำลังสร้างเมื่อชนิด enum มีการอ้างอิงสำหรับครั้งแรก


13
คุณควรเพิ่มโดยค่าเริ่มต้น enums มี Constructor ส่วนตัวโดยปริยายและไม่จำเป็นต้องเพิ่ม Constructor ส่วนตัวจนกว่าคุณจะมีรหัสที่คุณต้องใช้ใน Constructor นั้นจริงๆ
Nimrod Dayan

แต่ละฟิลด์ enum สร้างอินสแตนซ์เพียงครั้งเดียวดังนั้นจึงไม่จำเป็นต้องสร้างตัวสร้างส่วนตัว
Pravat Panda

2
สาธารณะ enum MySingleton {INSTANCE, INSTANCE1; } แล้วตามด้วย System.out.println (MySingleton.INSTANCE.hashCode ()); System.out.println (MySingleton.INSTANCE1.hashCode ()); มันพิมพ์แฮชโค้ดที่แตกต่างกัน หมายความว่ามีการสร้างวัตถุสองรายการของ MySingleton หรือไม่
กอ

@scottmiles ใช่ เพราะคุณมีสองกรณี ซิงเกิลโดยนิยามมีหนึ่ง
Elliott Frisch

โมดิprivateฟายเออร์ไม่มีความหมายสำหรับคอนenumสตรัคเตอร์และซ้ำซ้อนทั้งหมด
Dmitri Nesteruk

76

ประเภทเป็นชนิดพิเศษenumclass

คุณenumจะรวบรวมสิ่งที่ต้องการจริงๆ

public final class MySingleton {
    public final static MySingleton INSTANCE = new MySingleton();
    private MySingleton(){} 
}

เมื่อรหัสของคุณเข้าถึงครั้งแรกINSTANCEคลาสMySingletonจะถูกโหลดและเริ่มต้นโดย JVM กระบวนการนี้เริ่มต้นstaticเขตข้อมูลข้างต้นหนึ่งครั้ง (ขี้เกียจ)


คลาสนี้จะมีตัวสร้างส่วนตัว () หรือไม่ ฉันคิดว่าคงจะเป็น
Yasir Shabbir Choudhary

public enum MySingleton { INSTANCE,INSTANCE1; }จากนั้นSystem.out.println(MySingleton.INSTANCE.hashCode()); System.out.println(MySingleton.INSTANCE1.hashCode());มันจะพิมพ์ hashcodes ที่ต่างกัน หมายความว่ามีการสร้างวัตถุสองรายการของ MySingleton หรือไม่
กอ

2
@scottmiles ใช่นั่นเป็นเพียงenumค่าคงที่ที่ต่างกันสองค่า
Sotirios Delimanolis

2
@caf แน่นอน ดูที่นี่: docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3
Sotirios Delimanolis

@SotiriosDelimanolis เป็นวิธีการที่มีความปลอดภัย enum ไหม?
abg

63

ในหนังสือแนวปฏิบัติที่ดีที่สุดของ Java นี้โดย Joshua Bloch คุณสามารถค้นหาคำอธิบายว่าทำไมคุณควรบังคับใช้คุณสมบัติ Singleton ด้วย Constructor ส่วนตัวหรือประเภท Enum บทนี้ค่อนข้างยาวดังนั้นจึงสรุปได้ว่า:

การสร้างคลาสเป็น Singleton สามารถทำให้ยากต่อการทดสอบลูกค้าเนื่องจากเป็นไปไม่ได้ที่จะทดแทนการใช้งานจำลองสำหรับ singleton เว้นแต่จะใช้อินเทอร์เฟซที่ทำหน้าที่เป็นประเภทของมัน วิธีที่แนะนำคือการนำ Singletons ไปใช้โดยสร้าง enum กับองค์ประกอบเดียว:

// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}

วิธีการนี้เทียบเท่ากับการใช้งานในพื้นที่สาธารณะยกเว้นว่ามีความรัดกุมมากกว่านั้นให้บริการการทำให้เป็นอนุกรมฟรีและให้การรับประกันที่แข็งแกร่งต่อการโต้ตอบหลายครั้งแม้ในการเผชิญหน้าต่อเนื่องที่ซับซ้อนหรือการสะท้อนการโจมตี

ในขณะที่วิธีการนี้ยังไม่ได้นำมาใช้อย่างกว้างขวางประเภท enum องค์ประกอบเดียวเป็นวิธีที่ดีที่สุดในการใช้ซิงเกิล


ฉันคิดว่าการทดสอบเดี่ยวคุณสามารถใช้รูปแบบกลยุทธ์และให้การกระทำของซิงเกิลทั้งหมดผ่านกลยุทธ์ ดังนั้นคุณสามารถมี "การผลิต" กลยุทธ์หนึ่งและ "ทดสอบ" กลยุทธ์ ...
Erk

9

เช่นเดียวกับกรณี enum ทั้งหมด, Java instantiates แต่ละวัตถุเมื่อเรียนถูกโหลดด้วยการรับประกันบางอย่างที่มัน instantiated ครั้งว่าต่อJVM คิดว่าการINSTANCEประกาศเป็นเขตข้อมูลสุดท้ายคงที่สาธารณะ: Java จะยกตัวอย่างวัตถุในครั้งแรกที่มีการเรียกชั้นเรียน

กรณีที่ถูกสร้างขึ้นในระหว่างการเตรียมแบบคงที่ซึ่งถูกกำหนดไว้ในJava Language ข้อกำหนดมาตรา 12.4

สำหรับสิ่งที่คุ้มค่า, โจชัวโบลชอธิบายรูปแบบนี้ในรายละเอียดเป็นรายการที่ 3 ของที่มีประสิทธิภาพ Java Second Edition


6

เนื่องจากรูปแบบของซิงเกิลตันนั้นเกี่ยวกับการมีคอนสตรัคเตอร์ส่วนตัวและเรียกวิธีการบางอย่างเพื่อควบคุมอินสแตนซ์ (เช่นบางอย่างgetInstance) ใน Enums เรามีคอนสตรัคเตอร์ส่วนตัวโดยปริยาย

ฉันไม่รู้แน่ชัดว่าJVMหรือคอนเทนเนอร์บางตัวควบคุมอินสแตนซ์ของเราได้Enumsอย่างไร แต่ดูเหมือนว่ามันใช้งานโดยนัยSingleton Patternแล้วความแตกต่างคือเราไม่เรียกว่า a getInstanceเราแค่เรียก Enum


3

ดังที่ได้กล่าวมาแล้ว enum เป็นคลาส java ที่มีเงื่อนไขพิเศษที่นิยามของมันต้องเริ่มต้นด้วย "enum constant" อย่างน้อยหนึ่งตัว

นอกเหนือจากนั้นและ enums ไม่สามารถขยายหรือใช้เพื่อขยายคลาสอื่น ๆ enum เป็นคลาสที่เหมือนคลาสใด ๆ และคุณใช้โดยการเพิ่มเมธอดใต้คำจำกัดความคงที่:

public enum MySingleton {
    INSTANCE;

    public void doSomething() { ... }

    public synchronized String getSomething() { return something; }

    private String something;
}

คุณสามารถเข้าถึงวิธีการของซิงเกิลในบรรทัดเหล่านี้:

MySingleton.INSTANCE.doSomething();
String something = MySingleton.INSTANCE.getSomething();

การใช้ enum แทนที่จะเป็นคลาสนั้นดังที่ได้กล่าวไว้ในคำตอบอื่น ๆ ส่วนใหญ่เกี่ยวกับการสร้างอินสแตนซ์ของเธรดที่ปลอดภัยสำหรับเธรดและรับประกันได้ว่ามันจะเป็นเพียงหนึ่งสำเนาเสมอ

และที่สำคัญที่สุดคือพฤติกรรมนี้รับประกันโดย JVM เองและข้อมูลจำเพาะ Java

นี่คือส่วนหนึ่งจากข้อกำหนดคุณสมบัติ Javaเกี่ยวกับวิธีป้องกันอินสแตนซ์ enum หลายอินสแตนซ์:

ประเภท enum ไม่มีอินสแตนซ์อื่นนอกเหนือจากที่กำหนดโดยค่าคงที่ enum เป็นข้อผิดพลาดในการคอมไพล์เวลาพยายามที่จะสร้างอินสแตนซ์ประเภท enum อย่างชัดเจน วิธีการโคลนสุดท้ายใน Enum ทำให้มั่นใจได้ว่าค่าคงที่ของ enum จะไม่สามารถถูกโคลนได้และการดูแลเป็นพิเศษโดยกลไกการทำให้เป็นอนุกรมจะทำให้แน่ใจว่าอินสแตนซ์ที่ซ้ำกันจะไม่ถูกสร้างขึ้นเนื่องจากการดีซีเรียลไลเซชัน ห้ามมิให้มีการสะท้อนกลับของชนิด enum ร่วมกันสิ่งทั้งสี่นี้ทำให้แน่ใจว่าไม่มีอินสแตนซ์ประเภท enum เกินกว่าที่กำหนดโดยค่าคงที่ enum

น่าสังเกตว่าหลังจาก instantiation ข้อกังวลเรื่องความปลอดภัยของเธรดต้องได้รับการจัดการเหมือนในคลาสอื่น ๆ ด้วยคีย์เวิร์ดที่ซิงโครไนซ์เป็นต้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.