คุณกำหนดคลาสของค่าคงที่ใน Java ได้อย่างไร?


90

สมมติว่าคุณจำเป็นต้องกำหนดคลาสซึ่งทั้งหมดนี้เป็นค่าคงที่

public static final String SOME_CONST = "SOME_VALUE";

วิธีที่ต้องการในการทำเช่นนี้คืออะไร?

  1. อินเตอร์เฟซ
  2. คลาสนามธรรม
  3. ชั้นสุดท้าย

ฉันควรใช้อันไหนและทำไม?


คำชี้แจงบางคำตอบ:

Enums - ฉันจะไม่ใช้ enums ฉันไม่ได้แจกแจงอะไรเลยเพียงแค่รวบรวมค่าคงที่บางส่วนซึ่งไม่เกี่ยวข้องกัน แต่อย่างใด

อินเทอร์เฟซ - ฉันจะไม่ตั้งคลาสใด ๆ เป็นคลาสที่ใช้อินเทอร์เฟซ ISomeInterface.SOME_CONSTเพียงแค่ต้องการที่จะใช้อินเตอร์เฟซที่จะเรียกค่าคงที่เช่นดังนั้น:


มีการอภิปรายที่คล้ายกันที่นี่: stackoverflow.com/questions/320588/… . ฉันจะใช้คลาสสุดท้ายกับคอนสตรัคเตอร์ส่วนตัวเพื่อที่จะไม่สามารถสร้างอินสแตนซ์ได้
Dan Dyer

2
ขอโทษที "ฉันจะไม่ใช้ enums" เปลี่ยนคำถามนี้เป็น "วิธีที่ดีที่สุดในการทำอะไรโง่ ๆ "
cletus

ฉันไม่ได้บอกว่าคุณจะใช้อินเทอร์เฟซ แต่ไม่มีจุดที่จะใช้อินเทอร์เฟซเพื่อทำเช่นนั้น ไปกับคลาสสุดท้าย
:)

Enum มีปัญหาอะไร? คุณสามารถใช้มันเพื่อรวบรวม 'ค่าคงที่บางส่วนที่ไม่เกี่ยวข้องกัน แต่อย่างใด' หืม?
gedevan

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

คำตอบ:


94

ใช้คลาสสุดท้าย เพื่อความง่ายคุณอาจใช้การนำเข้าแบบคงที่เพื่อนำค่าของคุณกลับมาใช้ในคลาสอื่น

public final class MyValues {
  public static final String VALUE1 = "foo";
  public static final String VALUE2 = "bar";
}

ในชั้นเรียนอื่น:

import static MyValues.*
//...

if(variable.equals(VALUE1)){
//...
}

4
ประโยชน์ของการสร้างคลาสแยกที่นี่อยู่ที่ไหน? แม้ว่าปกติแล้วฉันจะไม่ถือ Calendar API เป็นตัวอย่างที่ดีในการออกแบบ แต่ก็ใช้ได้ดีในแง่ของ "ค่าคงที่เกี่ยวกับปฏิทินอยู่ในคลาสปฏิทิน"
Jon Skeet

7
ประโยชน์คือการไม่ทำซ้ำรหัสในกรณีที่คุณต้องการใช้ค่าคงที่ซ้ำในคลาสมากกว่าหนึ่งคลาส ฉันเดาว่าคุณสามารถเห็นข้อดีของสิ่งนี้ได้อย่างง่ายดาย
user54579

2
ทำไมคุณต้องทำรหัสซ้ำ เพียงแค่อ้างถึงคลาสอื่น ๆ ฉันพบว่าค่าคงที่ค่อนข้างหายากจริงๆ
Jon Skeet

14
เพื่อให้อ่านง่ายขึ้นฉันยังมีตัวสร้างเริ่มต้นส่วนตัวที่ไม่มีเนื้อความ (และความคิดเห็นที่ตรงกัน)
Ran Biron

5
คำตอบที่ค่อนข้างเก่าและความคิดเห็นนี้อาจไม่ตรงจุด แต่ฉันจะใช้VALUE1.equals(variable)เพราะหลีกเลี่ยง NPE
Aakash

38

คำชี้แจงของคุณระบุว่า: "ฉันจะไม่ใช้ enums ฉันไม่ได้แจกแจงอะไรเลยเพียงแค่รวบรวมค่าคงที่บางส่วนที่ไม่เกี่ยวข้องกัน แต่อย่างใด"

ถ้าค่าคงที่ไม่เกี่ยวข้องกันเลยทำไมคุณถึงต้องการรวบรวมเข้าด้วยกัน? ใส่ค่าคงที่แต่ละค่าในคลาสที่เกี่ยวข้องมากที่สุด


2
จอน - พวกเขามีความเกี่ยวข้องกันในแง่ที่พวกเขาทั้งหมดเป็นฟังก์ชันเดียวกัน อย่างไรก็ตามไม่ใช่การแจกแจงสิ่งใด ๆ ... พวกเขาไม่ได้ถือคุณสมบัติว่าวัตถุคือ "หนึ่งในสิ่งเหล่านั้น"
Yuval Adam

13
ตกลง. ในกรณีนั้นให้ใส่ไว้ในคลาสที่ใกล้เคียงกับฟังก์ชันที่เกี่ยวข้องมากที่สุด
Jon Skeet

28

คำแนะนำของฉัน (ตามลำดับความต้องการที่ลดลง):

1) ไม่ทำมัน สร้างค่าคงที่ในคลาสจริงที่เกี่ยวข้องมากที่สุด การมีคลาส / อินเทอร์เฟซ 'กระเป๋าค่าคงที่' ไม่ได้เป็นไปตามแนวทางปฏิบัติที่ดีที่สุดของ OO

ฉันและคนอื่น ๆ ไม่สนใจ # 1 เป็นครั้งคราว หากคุณกำลังจะทำเช่นนั้น:

2) คลาสสุดท้ายที่มีตัวสร้างส่วนตัวอย่างน้อยที่สุดก็จะป้องกันไม่ให้ใครใช้ 'ถุงค่าคงที่' ของคุณในทางที่ผิดโดยการขยาย / ใช้งานเพื่อให้เข้าถึงค่าคงที่ได้ง่าย (ฉันรู้ว่าคุณบอกว่าจะไม่ทำแบบนี้ - แต่นั่นไม่ได้หมายความว่าจะมีคนมาตามหลังคุณจะไม่ทำ)

3) อินเทอร์เฟซสิ่งนี้จะใช้งานได้ แต่ไม่ใช่การตั้งค่าของฉันที่ให้กล่าวถึงการละเมิดที่เป็นไปได้ใน # 2

โดยทั่วไปเพียงเพราะสิ่งเหล่านี้เป็นค่าคงที่ไม่ได้หมายความว่าคุณไม่ควรใช้หลักการ oo ปกติกับพวกเขา ถ้าไม่มีใครสนใจค่าคงที่ แต่ชั้นเรียนควรเป็นแบบส่วนตัวและในชั้นเรียนนั้น หากเฉพาะการทดสอบดูแลเกี่ยวกับค่าคงที่ - ควรอยู่ในคลาสทดสอบไม่ใช่รหัสการผลิต หากมีการกำหนดค่าคงที่ในหลายตำแหน่ง (ไม่ใช่เพียงแค่ค่าเดียวกันโดยบังเอิญ) - refactor เพื่อกำจัดการทำซ้ำ และอื่น ๆ - ปฏิบัติต่อพวกเขาเหมือนที่คุณทำ


2
ปัญหาของ 1 คือบางครั้งคุณจะแทรกการอ้างอิงบางอย่างในโค้ดของคุณไปยังคลาสระดับอื่นเพื่อดึงค่าคงที่
amdev

ฟิลด์ทั้งหมดในอินเทอร์เฟซเป็นแบบสาธารณะโดยปริยายคงที่และขั้นสุดท้าย
Kodebetter

@Kodebetter แต่อินเทอร์เฟซสามารถขยาย / ใช้งานเพื่อเพิ่มฟิลด์เพิ่มเติมได้
วิทย์

12

ดังที่ Joshua Bloch บันทึกไว้ในภาษา Java ที่มีประสิทธิภาพ:

  • ควรใช้อินเทอร์เฟซเพื่อกำหนดประเภทเท่านั้น
  • คลาสนามธรรมไม่ได้ป้องกันความไม่แน่นอน (สามารถเป็นคลาสย่อยได้และยังแนะนำว่าคลาสเหล่านี้ได้รับการออกแบบให้เป็นคลาสย่อย)

คุณสามารถใช้ Enum ได้หากค่าคงที่ทั้งหมดของคุณมีความสัมพันธ์กัน (เช่นชื่อดาวเคราะห์) ใส่ค่าคงที่ในคลาสที่เกี่ยวข้อง (หากคุณสามารถเข้าถึงได้) หรือใช้คลาสยูทิลิตี้ที่ไม่เสถียร (กำหนดคอนสตรัคเตอร์เริ่มต้นส่วนตัว) .

class SomeConstants
{
    // Prevents instanciation of myself and my subclasses
    private SomeConstants() {}

    public final static String TOTO = "toto";
    public final static Integer TEN = 10;
    //...
}

จากนั้นตามที่ระบุไว้แล้วคุณสามารถใช้การนำเข้าแบบคงที่เพื่อใช้ค่าคงที่ของคุณ


7

วิธีที่ฉันชอบคืออย่าทำอย่างนั้นเลย อายุของค่าคงที่ค่อนข้างตายเมื่อ Java 5 เปิดตัว typesafe enums และก่อนหน้านั้น Josh Bloch ก็ได้เผยแพร่เวอร์ชันที่ใช้งานง่ายกว่าเล็กน้อยซึ่งทำงานบน Java 1.4 (และรุ่นก่อนหน้า)

หากคุณไม่ต้องการความสามารถในการทำงานร่วมกันกับรหัสเดิมบางอย่างก็ไม่มีเหตุผลที่จะใช้ค่าคงที่ชื่อสตริง / จำนวนเต็มอีกต่อไป


2
คำตอบที่ดีเป็นตัวอย่างจะดีกว่า
amdev

3

เพียงใช้คลาสสุดท้าย

หากคุณต้องการเพิ่มค่าอื่น ๆ ให้ใช้คลาสนามธรรม

มันไม่สมเหตุสมผลกับการใช้อินเทอร์เฟซอินเทอร์เฟซควรจะระบุสัญญา คุณแค่ต้องการประกาศค่าคงที่



3

enums สบายดี IIRC หนึ่งรายการใน Java ที่มีประสิทธิภาพ (2nd Ed) มีenumค่าคงที่ระบุอ็อพชันมาตรฐานที่ใช้ [คีย์เวิร์ด Java] interfaceสำหรับค่าใด ๆ

ตั้งค่าของฉันคือการใช้ [Java คำหลัก] interfaceกว่าfinal classสำหรับค่าคงที่ คุณจะได้รับไฟล์public static final. บางคนจะโต้แย้งว่าinterfaceอนุญาตให้โปรแกรมเมอร์ที่ไม่ดีนำไปใช้งานได้ แต่โปรแกรมเมอร์ที่ไม่ดีจะเขียนโค้ดที่ห่วยไม่ว่าคุณจะทำอะไรก็ตาม

ที่ดูดีกว่า?

public final class SomeStuff {
     private SomeStuff() {
         throw new Error();
     }
     public static final String SOME_CONST = "Some value or another, I don't know.";
}

หรือ:

public interface SomeStuff {
     String SOME_CONST = "Some value or another, I don't know.";
}

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

1
@ liltitus27 ฉันเห็นด้วยในระดับหนึ่ง แต่คุณต้องการรหัสอัปลักษณ์เพื่อหยุดวิธีหนึ่งของโปรแกรมเมอร์ที่ไม่ดีในการเขียนโค้ดที่ไม่ดีหรือไม่ รหัสที่พวกเขาสร้างจะยังคงสิ้นหวัง แต่ตอนนี้โค้ดของคุณอ่านได้น้อยลง
Tom Hawtin - แทคไลน์

1

หรือ 4. ใส่ไว้ในคลาสที่มีตรรกะที่ใช้ค่าคงที่มากที่สุด

... ขอโทษทนไม่ไหว ;-)


3
นี่ไม่ใช่ความคิดที่ดี สิ่งนี้สร้างการอ้างอิงระหว่างคลาสซึ่งไม่ควรมี
Yuval Adam

ทำไมจะไม่ล่ะ? ฉันจะบอกว่าคลาสที่ใช้ค่าคงที่เดียวกันก็มีการพึ่งพาอยู่ดี ... และอย่างใดก็ต้องมีคลาสหนึ่งที่ขึ้นอยู่กับค่าคงที่เหล่านี้มากที่สุด
Simon Groenewolt

หากมีเพียงคลาสเดียวที่ใช้ค่าคงที่สิ่งนี้ก็สมเหตุสมผล
Tom Hawtin - แทคไลน์

-1
  1. ข้อเสียอย่างหนึ่งของคอนสตรัคเตอร์ส่วนตัวคือวิธีการที่มีอยู่ไม่สามารถทดสอบได้

  2. Enum by the nature concept ดีที่จะนำไปใช้กับโดเมนบางประเภทนำไปใช้กับค่าคงที่แบบกระจายอำนาจดูไม่ดีพอ

แนวคิดของ Enum คือ "การแจงนับคือชุดของรายการที่เกี่ยวข้องอย่างใกล้ชิด"

  1. การขยาย / ใช้งานอินเทอร์เฟซแบบคงที่เป็นแนวทางปฏิบัติที่ไม่ดีเป็นการยากที่จะคิดถึงข้อกำหนดในการขยายค่าคงที่ไม่เปลี่ยนรูปแทนการอ้างถึงโดยตรง

  2. หากใช้เครื่องมือที่มีคุณภาพเช่น SonarSource มีกฎบังคับให้นักพัฒนาต้องวางอินเทอร์เฟซคงที่นี่เป็นเรื่องที่น่าอึดอัดใจเนื่องจากโครงการจำนวนมากเพลิดเพลินกับอินเทอร์เฟซที่คงที่และไม่ค่อยเห็นสิ่งที่ "ขยาย" เกิดขึ้นบนอินเทอร์เฟซที่คงที่


1
"ตัวสร้างส่วนตัวคือการมีอยู่ของวิธีการที่ไม่สามารถทดสอบได้": ไม่เป็นความจริง หากคุณ (หรือหัวหน้าของคุณ) หวาดระแวงอย่างมากเกี่ยวกับรายงานความครอบคลุมของรหัสถึง 100% คุณยังคงสามารถทดสอบได้โดยใช้การสะท้อนและตรวจสอบให้แน่ใจว่าตัวสร้างมีข้อยกเว้น แต่ IMHO นี่เป็นเพียงพิธีการและไม่จำเป็นต้องทดสอบจริงๆ หากคุณ (หรือทีม) สนใจเฉพาะสิ่งที่สำคัญจริงๆการครอบคลุมไลน์ 100% ก็ไม่จำเป็น
L. Holanda
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.