คลาสบูลีนของ Java - ทำไมไม่ enum?


11

สำหรับฉันแล้วดูเหมือนว่าคลาสบูลีนเป็นตัวเลือกที่เหมาะสมที่สุดที่จะนำไปใช้เป็น enum

การดูซอร์สโค้ดคลาสส่วนใหญ่เป็นวิธีสแตติกซึ่งสามารถย้ายได้ไม่เปลี่ยนแปลงไปเป็น enum ส่วนที่เหลือจะกลายเป็น enum ได้ง่ายกว่ามาก เปรียบเทียบต้นฉบับ (ความคิดเห็นและวิธีการแบบคงที่ออก):

public final class Boolean implements java.io.Serializable,
                                      Comparable<Boolean>
{
   public static final Boolean TRUE = new Boolean(true);
  public static final Boolean FALSE = new Boolean(false);
   private final boolean value;
   public Boolean(boolean value) {
       this.value = value;
   }
   public Boolean(String s) {
       this(toBoolean(s));
   }
   public boolean booleanValue() {
       return value;
   }
   public String toString() {
       return value ? "true" : "false";
   }
   public int hashCode() {
       return value ? 1231 : 1237;
   }
   public boolean equals(Object obj) {
       if (obj instanceof Boolean) {
           return value == ((Boolean)obj).booleanValue();
       }
       return false;
   }
   public int compareTo(Boolean b) {
       return compare(this.value, b.value);
   }
}

ด้วยรุ่น enum:

public enum Boolean implements Comparable<Boolean>
{
   FALSE(false), TRUE(true);
   private Boolean(boolean value) {
       this.value = value;
   }
   private final boolean value;
   public boolean booleanValue() {
       return value;
   }

   public String toString() {
       return value ? "true" : "false";
   }
}

มีเหตุผลใดที่บูลีนไม่สามารถกลายเป็น enum ได้?

หากนี่คือรหัส Sun เพื่อแทนที่เมธอด equals () มันจะขาดการตรวจสอบขั้นพื้นฐานอย่างมากในการเปรียบเทียบการอ้างอิงของวัตถุทั้งสองก่อนที่จะเปรียบเทียบค่าของพวกเขา นี่คือวิธีที่ฉันคิดว่าวิธี equals () ควรเป็น:

   public boolean equals(Object obj) {

       if (this == obj) {
          return true;
       }

       if (obj instanceof Boolean) {
           return value == ((Boolean)obj).booleanValue();
       }
       return false;
   }

4
คุณมองเห็นคุณค่าอื่นสำหรับบูลีนที่ไม่เป็นความจริงหรือเท็จ?

1
@MichaelT enum ไม่จำเป็นต้องมีค่ามากกว่า 2 มันจะไม่มีประโยชน์ใน Java เพราะมีคำสั่งพิเศษสำหรับการประมวลผล booleans ( if) แต่จากแนวคิดเชิงทฤษฎี / ประเภทมุมมอง booleans และ enums เป็นทั้งสองประเภทรวมดังนั้นฉันคิดว่ามันยุติธรรมที่จะถามว่าทำไมพวกเขาไม่ ไม่ลดช่องว่างระหว่างพวกเขา
Doval

1
หมายเหตุ: คุณดูเหมือนจะพลาดการใช้งานvalueOf(String)(ซึ่งจะขัดแย้งกับคุณค่าของ enum) และเวทย์มนตร์ที่อยู่เบื้องหลังgetBooleanซึ่งสามารถทำให้เกิดBoolean.valueOf("yes")ผลตอบแทนจริงมากกว่าเท็จ ทั้งสองอย่างนี้เป็นส่วนหนึ่งของข้อมูลจำเพาะ 1.0และจะต้องมีความเข้ากันได้ย้อนหลังที่เหมาะสม

8
@MichaelT FileNotFoundแน่นอน!
Donal Fellows

คำตอบ:


13

ฉันเดาว่าฉันสามารถเริ่มต้นได้ด้วยการพิสูจน์ว่าการแจกแจง Java ไม่ได้ถูกเพิ่มในภาษาการเขียนโปรแกรม Java จนกระทั่ง JDK 1.5 และดังนั้นการแก้ปัญหานี้ไม่ได้เป็นทางเลือกในวันแรก ๆ เมื่อมีการกำหนดคลาสบูลีน

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


3
คุณอาจพบข้อมูลจำเพาะภาษาJava 1.0สำหรับ java.lang.Boolean เพื่อช่วย new Boolean("True")และnew Boolean("true")ยังอาจทำให้เกิดปัญหาบางอย่างกับการดำเนินงาน enum สมมุติ

ดูเหมือนว่าผิดที่จะอนุญาตวัตถุหลาย ๆ อัน (ไม่เปลี่ยนรูป) และดังนั้นการใช้ Constructors ที่ให้มาในบูลีนจึงไม่ใช่ความคิดที่ดี - ดังที่เอกสาร API ระบุ
Highland Mark

ข้อมูลจำเพาะภาษาไม่ได้ช่วยในคำถามประเภทนี้เนื่องจากไม่ได้ระบุการใช้งานของคลาส นี่คือวิธีที่ดีที่สุดในการใช้ข้อมูลจำเพาะ
Highland Mark

13

มีบางอย่างที่ใช้งานไม่ได้และไม่ได้ทำงานในรูปแบบที่น่าแปลกใจเมื่อเปรียบเทียบกับฟังก์ชันการทำงานก่อนหน้าของบูลีนของ Java

เราจะไม่สนใจการชกมวยเนื่องจากเป็นสิ่งที่เพิ่มใน 1.5 สมมุติฐานถ้าดวงอาทิตย์อยากจะพวกเขาจะได้ทำให้การทำงานเช่นเดียวกับมวยดำเนินการในenum Booleanclass Boolean

กระนั้นก็มีวิธีอื่นที่น่าแปลกใจ (กับ coder) ที่จะเกิดขึ้นทันทีเมื่อเทียบกับการทำงานของคลาสก่อนหน้า

ปัญหา valueOf (String)

ตัวอย่างง่ายๆของสิ่งนี้คือ:

public class BooleanStuff {
    public static void main(String args[]) {
        Boolean foo = Boolean.valueOf("TRUE");
        System.out.println(foo);
        foo = Boolean.valueOf("TrUe");
        System.out.println(foo);
        foo = Boolean.valueOf("yes");  // this is actually false
        System.out.println(foo);

        // Above this line is perfectly acceptable Java 1.3
        // Below this line takes Java 1.5 or later

        MyBoolean bar;
        bar = MyBoolean.valueOf("FALSE");
        System.out.println(bar);
        bar = MyBoolean.valueOf("FaLsE");
        System.out.println(bar);
    }

    enum MyBoolean implements Comparable<MyBoolean> {
        FALSE(false), TRUE(true);
        private MyBoolean(boolean value) { this.value = value; }
        private final boolean value;
        public boolean booleanValue() { return value; }
        public String toString() { return value ? "true" : "false"; }
    }
}

การทำงานของรหัสนี้ให้:

จริง
จริง
เท็จ
เท็จ
ข้อยกเว้นในเธรด "main" java.lang.IllegalArgumentException: ไม่มีค่าคงที่ enum BooleanStuff.MyBoolean.FaLsE
    ที่ java.lang.Enum.valueOf (Enum.java:236)
    ที่ BooleanStuff $ MyBoolean.valueOf (BooleanStuff.java:17)
    ที่ BooleanStuff.main (BooleanStuff.java:13)

นี่คือปัญหาที่ผมไม่สามารถผ่านอะไรที่ไม่TRUEหรือจะFALSEvalueOf(String)

ไม่เป็นไร ... เราจะลบล้างมันด้วยวิธีการของเราเอง ...

    public static MyBoolean valueOf(String arg) {
        return arg.equalsIgnoreCase("true") ? TRUE : FALSE;
    }

แต่ ... มีปัญหาที่นี่ คุณไม่สามารถแทนที่วิธีการคง

ดังนั้นรหัสทั้งหมดที่ผ่านไปมาtrueหรือTrueกรณีอื่น ๆ ผสมกันจะผิดพลาด - และค่อนข้างน่าตื่นตากับข้อยกเว้นรันไทม์

สนุกไปกับ valueOf

มีบิตอื่น ๆ ที่ไม่ทำงานได้ดีเกินไป:

public static void main(String args[]) {
    Boolean foo = Boolean.valueOf(Boolean.valueOf("TRUE"));
    System.out.println(foo);

    MyBoolean bar = MyBoolean.valueOf(MyBoolean.valueOf("FALSE"));
    System.out.println(bar);
}

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

ข้อผิดพลาด: (7, 24) java: ไม่พบวิธีการที่เหมาะสมสำหรับ valueOf (BooleanStuff.MyBoolean)
    เมธอด BooleanStuff.MyBoolean.valueOf (java.lang.String) ไม่สามารถใช้ได้
      (อาร์กิวเมนต์จริง BooleanStuff.MyBoolean ไม่สามารถแปลงเป็น java.lang.String โดยการแปลงการเรียกใช้เมธอด)
    เมธอด java.lang.Enum.valueOf (java.lang.Class, java.lang.String) ไม่สามารถใช้ได้
      (ไม่สามารถสร้างอินสแตนซ์จากอาร์กิวเมนต์ได้เนื่องจากรายการอาร์กิวเมนต์ที่เกิดขึ้นจริงและเป็นทางการแตกต่างกันในความยาว)

หากเราบีบบังคับข้อผิดพลาดทางไวยากรณ์นั้นกลับเป็นStringประเภท:

public static void main(String args[]) {
    Boolean foo = Boolean.valueOf(Boolean.valueOf("TRUE"));
    System.out.println(foo);

    MyBoolean bar = MyBoolean.valueOf(MyBoolean.valueOf("FALSE").toString());
    System.out.println(bar);
}

เราได้รับข้อผิดพลาด runtime ของเรากลับมา:

จริง
ข้อยกเว้นในเธรด "main" java.lang.IllegalArgumentException: ไม่มีค่าคงที่ enum BooleanStuff.MyBoolean.false
    ที่ java.lang.Enum.valueOf (Enum.java:236)
    ที่ BooleanStuff $ MyBoolean.valueOf (BooleanStuff.java:11)
    ที่ BooleanStuff.main (BooleanStuff.java:7)

ทำไมทุกคนจะเขียนอย่างนั้น? Dunno ... แต่เป็นรหัสที่ใช้ในการทำงานและจะไม่ทำงานอีกต่อไป


อย่าเข้าใจฉันผิดฉันชอบความคิดของวัตถุที่ไม่เปลี่ยนรูปที่ให้มาเพียงชุดเดียว Enum แก้ปัญหานี้ได้ ฉันพบรหัสผู้จำหน่ายที่มีข้อบกพร่องจากรหัสผู้จำหน่ายที่มีลักษณะเช่นนี้:

if(boolValue == new Boolean("true")) { ... }

ที่ไม่เคยทำงาน(ไม่ฉันไม่สามารถแก้ไขได้เพราะรัฐไม่ถูกต้องได้รับการแก้ไขที่อื่นและแก้ไขนี้ยากจนว่าในรูปแบบที่แปลกที่ผมไม่ได้มีเวลาที่จะแก้ปัญหา) ถ้านี่คือ enum รหัสนั้นจะใช้ได้แทน

อย่างไรก็ตามความจำเป็นของไวยากรณ์รอบ enum (case sensitive - ขุดลงในenumConstantDirectoryเบื้องหลังvalueOfข้อผิดพลาดรันไทม์ที่จำเป็นต้องทำงานในทางที่ enums อื่น ๆ ) และวิธีการทำงานแบบคงที่ทำให้จำนวนของสิ่งที่จะทำลายที่ป้องกันไม่ให้มัน เป็นแบบหล่นแทน Boolean


1
หากมีใครออกแบบมาตั้งแต่เริ่มต้นภาษาใหม่ด้วยความรู้เกี่ยวกับวิธีการทำงานของ Java (และไม่ได้) การมีชนิดของวัตถุบูลีนเป็นโครงสร้างแบบ enum ... มันไม่เหมาะกับวิธีการทำงานของจาวาในขณะนี้ ฉันแน่ใจว่ามีนักออกแบบภาษาบางคนเตะมันด้วยตัวเอง ถ้าใครสามารถเริ่มต้นใหม่ด้วย Java 8 และสิ่งต่าง ๆ เช่นวิธีการเริ่มต้นในส่วนต่อประสานฉันแน่ใจว่ามีความผิดพลาดหลายอย่างของ Java ที่สามารถทำได้ค่อนข้างดีกว่า - ในเวลาเดียวกันฉันรู้สึกซาบซึ้งจริงๆ รหัส Java 1.3 บางส่วนและยังคงรวบรวมใน 1.8 - และนั่นคือสิ่งที่เราตอนนี้

มันจะไม่ยากมากที่จะเพิ่มofหรือfromวิธีการและ javadoc ที่เหมาะสม
assylias

@assylias ประชุมมีมากของรหัส Java อื่น ๆvalueOfและ Boolean.valueOf () ได้รับมีตั้งแต่ 1.0 Enums จะไม่สามารถใช้ valueOf เป็นวิธีการคงที่หรือบูลีนจะต้องใช้วิธีที่แตกต่างจากวิธีที่มันใช้อยู่ ทำการแบ่งการประชุมหรือการทำงานร่วมกัน - และไม่มีบูลีนเป็นตัวแบ่ง enum ไม่ จากนี้ทางเลือกค่อนข้างง่าย

"แต่ ... มีปัญหาที่นี่คุณไม่สามารถแทนที่วิธีคงที่" คุณไม่ได้ "เอาชนะ" อะไรเลย - วิธีนี้ไม่มีอยู่ในซูเปอร์คลาส ปัญหาแทนคือวิธีการกำหนดโดยอัตโนมัติสำหรับ enums ทั้งหมดและคุณไม่สามารถกำหนดใหม่ได้
user102008

"สำหรับ foo ฉันเพิ่งได้รับคำเตือนเกี่ยวกับการชกมวยค่าที่บรรจุอยู่ในกล่องแล้วอย่างไรก็ตามรหัสสำหรับแถบนั้นเป็นข้อผิดพลาดทางไวยากรณ์:" นี่เป็นการเปรียบเทียบที่ไม่ถูกต้อง ในBoolean.valueOf(Boolean.valueOf("TRUE"))ที่มีแตกต่างกันสอง valueOfวิธี: และvalueOf(String) valueOf(boolean)ไวยากรณ์ผิดพลาดเป็นเพราะคุณลืมที่จะใช้ในvalueOf(boolean) MyBooleanจากนั้นจะมีการเชื่อมต่ออัตโนมัติระหว่างการโทรสองครั้งซึ่งเป็นภาษาที่เข้ารหัสยากBooleanแต่ไม่ใช่MyBooleanหากคุณใช้valueOf(boolean) MyBoolean.valueOf(MyBoolean.valueOf("FALSE").booleanValue())งานได้
user102008

2

เป็นไปได้มากว่าเป็นเพราะรุ่นดั้งเดิมbooleanไม่ได้เป็นEnumและรุ่นดั้งเดิมของชนิดบรรจุกล่องทำงานเกือบเหมือนกันกับรุ่นที่ไม่มีกล่องของพวกเขา เช่น

Integer x = 5;
Integer y = 7;
Integer z = x + y;

(ประสิทธิภาพอาจไม่เหมือนกัน แต่เป็นวิชาอื่น)

มันคงจะแปลกถ้าคุณเขียนได้:

Boolean b = Boolean.TRUE;
switch (b) {
case Boolean.TRUE:
    // do things
    break;
case Boolean.FALSE:
    // do things
    break;
}

แต่ไม่:

boolean b = true;
switch(b) {
case true:
    // do things
    break;
case false:
    // do things
    break;
}  

1
คุณอาจต้องการแสดงคำสั่ง if ที่ไม่สามารถใช้ได้กับ enum เช่นกัน

@MichaelT ฉันคิดว่าคอมไพเลอร์จะยังคงสามารถนำออกจากกล่องและifทำงานได้เหมือนที่เป็นอยู่ในปัจจุบัน ในทางกลับกันไม่มีทางที่จะเพิกเฉยต่อความจริงที่ว่าคุณได้เพิ่มฟังก์ชั่นพิเศษให้กับBooleanสิ่งที่booleanไม่มี
Doval

โอ้โห ... คุณไม่สามารถเขียนคำสั่ง switch สำหรับ booleans ใน Java ได้ไหม? มันบ้ามาก
Thomas Eding

ชั้นเรียนมวยเท่านั้นทำหน้าที่เหมือนดั้งเดิมเนื่องจากการ unboxing จำนวนเต็มไม่มีโอเปอเรเตอร์ +
Highland Mark

@ HighlandMark นั่นเป็นเรื่องจริง แต่ประเด็นของฉันคือพวกเขาต้องเผชิญกับความเจ็บปวดอย่างมากเพื่อให้แน่ใจว่ากล่องชนิดนั้นสามารถใช้งานได้ในลักษณะเดียวกับคู่หูแบบดั้งเดิมของพวกเขา การแกะกล่องเป็นสิ่งที่พวกเขาต้องนำไปใช้มันไม่ได้มาฟรี
Doval

0

นอกเหนือจากการvalueOfออก (ซึ่งเป็นปัญหาในระดับ Java ก็สามารถทำงานได้ดีในระดับ JVM) มันเป็นเพราะBooleanมีคอนสตรัคสาธารณะ นั่นเป็นความคิดที่ไม่ดีซึ่งเลิกใช้แล้วในขณะนี้ แต่เป็นสิ่งที่อยู่ที่นี่


0

เหตุผลคือ "บูล" เป็นส่วนหนึ่งของภาษาจาวาที่เร็วกว่า "enum" เป็นเวลาหลายปีที่ "บูล" เป็นที่พึงปรารถนาอย่างยิ่งที่จะมีในขณะที่ "อีนูม" ไม่สามารถใช้ได้ เฉพาะตอนนี้คุณสามารถพูดว่า "ถ้า enum พร้อมใช้งานตั้งแต่เริ่มต้นจากนั้นเราสามารถใช้ bool เป็น enum แทนที่จะเป็นประเภทอื่น"

ใน Swift ซึ่งสามารถแสดง "bool" เป็น enum มีโครงสร้างสามชื่อ "Bool", "DarwinBoolean" และ "ObjCBool" ที่ใช้โพรโทคอล "ExpressibleByBooleanLiteral" (DarwinBoolean เข้ากันได้กับบูล C หรือ C ++, ObjCBool ​​เข้ากันได้กับ Objective-C BOOL) "true" และ "false" เป็นค่าพิเศษที่คอมไพเลอร์รู้จักและสามารถใช้เพื่อเริ่มต้นวัตถุที่สนับสนุนโปรโตคอล "ExpressibleByBooleanLiteral" เท่านั้น บูลมีตัวแปรภายใน "_value" ที่มีจำนวนเต็มหนึ่งบิต

ดังนั้น Bool ไม่ได้เป็นส่วนหนึ่งของภาษา Swift แต่เป็นของไลบรารีมาตรฐาน จริงและเท็จเป็นส่วนหนึ่งของภาษาและเป็นโปรโตคอล ExpressibleByBooleanLiteral

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