Kelas Boolean Java - mengapa tidak enum?

11

Tampak bagi saya bahwa kelas Boolean adalah kandidat ideal untuk diimplementasikan sebagai enum.

Melihat kode sumber, sebagian besar kelas adalah metode statis yang dapat dipindahkan tidak berubah ke enum, sisanya menjadi lebih sederhana sebagai enum. Bandingkan yang asli (komentar dan metode statis dihapus):

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);
   }
}

dengan versi 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";
   }
}

Apakah ada alasan mengapa Boolean tidak bisa menjadi enum?

Jika ini adalah kode Sun untuk menimpa metode equals (), maka tidak ada pemeriksaan mendasar untuk membandingkan referensi dari dua objek sebelum membandingkan nilainya. Beginilah menurut saya metode equals () seharusnya:

   public boolean equals(Object obj) {

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

       if (obj instanceof Boolean) {
           return value == ((Boolean)obj).booleanValue();
       }
       return false;
   }
Tanda Dataran Tinggi
sumber
4
Apakah Anda memperkirakan nilai lain untuk boolean yang tidak benar atau salah?
1
@MichaelT Enum tidak perlu memiliki lebih dari 2 nilai. Ini akan menjadi semacam tidak ada gunanya di Jawa karena memiliki pernyataan khusus untuk memproses boolean ( if) tetapi dari sudut pandang teori konseptual / tipe boolean dan enum keduanya adalah contoh tipe penjumlahan, jadi saya pikir wajar untuk bertanya mengapa mereka tidak akan menjembatani kesenjangan di antara mereka.
Doval
1
Catatan: Anda juga tampaknya telah melewatkan implementasi valueOf(String)(yang akan bertentangan dengan nilai enum dari) dan keajaiban di belakang getBooleanyang dapat membuatnya sehingga Boolean.valueOf("yes")mengembalikan true daripada false. Keduanya merupakan bagian dari spesifikasi 1.0 dan akan membutuhkan kompatibilitas ke belakang yang sesuai.
8
@MichaelT FileNotFound tentu saja!
Donal Fellows

Jawaban:

13

Yah, saya kira saya bisa mulai dengan berpendapat bahwa enumerasi Java tidak ditambahkan ke bahasa pemrograman Java sampai JDK 1.5. dan karena itu solusi ini bahkan bukan alternatif di masa-masa awal ketika kelas Boolean didefinisikan.

Yang sedang berkata, Java memiliki reputasi menjaga kompatibilitas ke belakang antara rilis dan, bahkan jika kami, hari ini, dapat mempertimbangkan solusi Anda sebagai alternatif yang baik, kami tidak dapat melakukannya tanpa memutus ribuan baris kode di luar sana yang sudah menggunakan Boolean lama kelas.

edalorzo
sumber
3
Anda mungkin menemukan spesifikasi bahasa Java 1.0 untuk java.lang.Boolean bisa membantu. Itu new Boolean("True")dan new Boolean("true")juga dapat menyebabkan beberapa masalah dengan implementasi enum hipotetis.
Tampaknya salah untuk mengizinkan beberapa objek (tidak dapat diubah), dan menggunakan Konstruktor yang disediakan pada Boolean bukanlah ide yang baik - seperti yang dikatakan dokumentasi API.
Highland Mark
Spek bahasa tidak akan membantu dengan pertanyaan semacam ini karena tidak menentukan implementasi Kelas. Inilah cara terbaik untuk mengimplementasikan spesifikasi.
Highland Mark
13

Ada beberapa hal yang tidak berfungsi, dan tidak bekerja dengan cara yang agak mengejutkan ketika Anda membandingkannya dengan fungsi boolean Java sebelumnya.

Kita akan mengabaikan tinju karena itu adalah sesuatu yang ditambahkan dengan 1,5. Hipotetis, jika Sun ingin mereka bisa membuat enum Booleanuntuk berperilaku seperti tinju yang dilakukan di class Boolean.

Namun, ada cara mengejutkan lainnya (ke pembuat kode) yang tiba-tiba akan rusak dibandingkan dengan fungsi dari kelas sebelumnya.

Masalah valueOf (String)

Contoh sederhana dari ini adalah:

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"; }
    }
}

Menjalankan kode ini memberikan:

benar
benar
Salah
Salah
Pengecualian di utas "utama" java.lang.IllegalArgumentException: Tidak ada konstanta BooleanStuff.MyBoolean.FaLsE konstan
    di java.lang.Enum.valueOf (Enum.java .: 36)
    di BooleanStuff $ MyBoolean.valueOf (BooleanStuff.java:17)
    di BooleanStuff.main (BooleanStuff.java:13)

Masalahnya di sini adalah bahwa saya tidak bisa melewati apa pun yang tidak TRUEatau FALSEuntuk valueOf(String).

Tidak apa-apa ... kita hanya akan menimpanya dengan metode kita sendiri ...

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

Tapi ... ada masalah di sini. Anda tidak dapat mengganti metode statis .

Jadi, semua kode yang diedarkan trueatau Truekasus campuran lainnya akan error - dan sangat spektakuler dengan pengecualian runtime.

Lebih menyenangkan dengan valueOf

Ada beberapa bit lain yang tidak berfungsi dengan baik:

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);
}

Sebab foo, saya baru saja mendapat peringatan tentang tinju nilai yang sudah kemas. Namun, kode untuk bilah adalah kesalahan sintaksis:

Kesalahan: (7, 24) java: tidak ditemukan metode yang cocok untuk valueOf (BooleanStuff.MyBoolean)
    metode BooleanStuff.MyBoolean.valueOf (java.lang.String) tidak berlaku
      (argumen aktual BooleanStuff.MyBoolean tidak dapat dikonversi ke java.lang.String dengan metode konversi doa)
    metode java.lang.Enum.valueOf (java.lang.Class, java.lang.String) tidak berlaku
      (tidak dapat instantiate dari argumen karena panjangnya daftar argumen aktual dan formal)

Jika kami memaksa kesalahan sintaks itu kembali ke Stringjenis:

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);
}

Kami mendapatkan kesalahan runtime kami kembali:

benar
Pengecualian di utas "utama" java.lang.IllegalArgumentException: Tidak ada konstanta BooleanStuff.MyBoolean.false
    di java.lang.Enum.valueOf (Enum.java .: 36)
    di BooleanStuff $ MyBoolean.valueOf (BooleanStuff.java:11)
    di BooleanStuff.main (BooleanStuff.java:7)

Mengapa ada orang yang menulis itu? Entahlah ... tetapi kodenya yang dulu berfungsi dan tidak lagi berfungsi.


Jangan salah paham, saya benar-benar menyukai gagasan hanya satu salinan dari objek abadi yang diberikan. Enum memecahkan masalah ini. Saya pribadi menemukan kode vendor yang memiliki bug di dalamnya dari kode vendor yang terlihat seperti ini:

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

itu tidak pernah berhasil (Tidak, saya tidak memperbaikinya karena keadaan yang salah diperbaiki di tempat lain, dan memperbaiki ini mematahkan hal itu dengan cara yang aneh bahwa saya benar-benar tidak punya waktu untuk men-debug) . Jika ini adalah enum, kode itu akan berfungsi sebagai gantinya.

Namun, kebutuhan sintaksis di sekitar enum (case sensitif - gali ke dalam enumConstantDirectory di belakang valueOf, kesalahan runtime yang perlu bekerja seperti itu untuk enum lain) dan cara metode statis bekerja menyebabkan sejumlah hal rusak yang mencegahnya dari menjadi setetes pengganti Boolean.

Komunitas
sumber
1
Jika seseorang mendesain, dari awal bahasa baru dengan pengetahuan tentang bagaimana Java bekerja (dan tidak), memiliki tipe objek Boolean menjadi struktur seperti enum ... itu hanya tidak cukup cocok dengan bagaimana Java bekerja sekarang. Saya yakin ada beberapa desainer bahasa yang berusaha keras untuk itu. Jika seseorang dapat memulai kembali dengan Java 8 dan hal-hal seperti metode default pada antarmuka, saya yakin bahwa banyak kesalahan Java bisa dilakukan dengan sedikit lebih bersih - pada saat yang sama saya sangat menghargai bisa mengambil beberapa kode Java 1.3 dan masih mengkompilasinya di 1.8 - dan di situlah kita sekarang.
Tidak akan terlalu sulit untuk menambahkan ofatau frommetode dan javadoc yang sesuai.
assylias
@assylias konvensi dengan banyak kode java lainnya adalah valueOfdan Boolean.valueOf () telah ada sejak 1.0 . Entah Enums tidak akan dapat menggunakan valueOf sebagai metode statis, atau Boolean akan membutuhkan metode yang berbeda dari yang telah digunakan. Melakukan istirahat konvensi atau kompatibilitas - dan tidak memiliki Boolean menjadi istirahat enum juga. Dari sini, pilihannya cukup sederhana.
"Tapi ... ada masalah di sini. Kamu tidak bisa mengganti metode statis." Anda tidak "mengesampingkan" apa pun - metode ini toh tidak ada dalam superclass. Masalahnya adalah bahwa metode ini secara otomatis ditentukan untuk semua enum, dan Anda tidak dapat mendefinisikannya kembali.
user102008
"Untuk foo, saya baru saja mendapat peringatan tentang tinju nilai yang sudah dikotakkan. Namun, kode untuk bilah adalah kesalahan sintaksis:" Ini adalah perbandingan yang salah. Di Boolean.valueOf(Boolean.valueOf("TRUE")), ada dua valueOf metode yang berbeda : valueOf(String)dan valueOf(boolean). Kesalahan sintaksis adalah karena Anda lupa mengimplementasikan valueOf(boolean)in MyBoolean. Lalu ada autounboxing antara dua panggilan, yang hardcoded dalam bahasa untuk Booleantetapi tidak MyBoolean. Jika Anda menerapkan valueOf(boolean) MyBoolean.valueOf(MyBoolean.valueOf("FALSE").booleanValue())karya
user102008
2

Kemungkinan besar karena booleantipe primitif bukan Enum, dan versi kotak tipe primitif berperilaku hampir identik dengan versi unboxed mereka. Misalnya

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

(Performanya mungkin tidak sama, tapi itu subjek yang berbeda.)

Agak aneh jika Anda bisa menulis:

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

tapi tidak:

boolean b = true;
switch(b) {
case true:
    // do things
    break;
case false:
    // do things
    break;
}  
Doval
sumber
1
Anda mungkin ingin menunjukkan pernyataan if yang tidak akan bekerja dengan enum juga.
@MichaelT Saya membayangkan kompiler masih akan dapat membuka kotaknya dan membuat ifpekerjaan seperti saat ini. Di sisi lain, tidak ada cara untuk mengabaikan fakta bahwa Anda telah menambahkan fungsionalitas tambahan Booleanyang booleantidak dimiliki.
Doval
Whoa ... Anda tidak bisa menulis pernyataan switch untuk boolean di Jawa? Itu gila.
Thomas Eding
Kelas-kelas tinju hanya bertindak seperti primitif karena unboxing. Integer tidak memiliki operator +.
Highland Mark
@ HighlandMark Itu benar, tetapi poin saya adalah bahwa mereka melewati kesulitan besar untuk memastikan tipe kotak dapat digunakan dengan cara yang sama seperti rekan-rekan primitif mereka. Membuka kotak adalah sesuatu yang harus mereka terapkan, itu tidak datang secara gratis.
Doval
0

Selain valueOfmasalah (yang merupakan masalah di tingkat Jawa, itu bisa berfungsi dengan baik di tingkat JVM), itu karena Booleanmemiliki konstruktor publik. Itu adalah ide yang buruk, saat ini sudah usang, tetapi itu adalah salah satu yang tinggal di sini.

Konrad Borowski
sumber
0

Alasannya adalah bahwa "bool" adalah bagian dari bahasa Jawa jauh lebih awal daripada "enum". Selama bertahun-tahun, "bool" sangat diinginkan untuk dimiliki, sementara "enum" tidak tersedia. Hanya sekarang Anda dapat mengatakan "jika enum telah tersedia sejak awal, maka kami bisa menerapkan bool sebagai enum daripada tipe yang terpisah".

Di Swift, yang bisa menyatakan "bool" sebagai enum, ada tiga struct bernama "Bool", "DarwinBoolean" dan "ObjCBool" yang mengimplementasikan protokol "ExpressibleByBooleanLiteral". (DarwinBoolean kompatibel dengan bool C atau C ++, ObjCBool ​​kompatibel dengan Objective-C BOOL). "true" dan "false" adalah nilai khusus yang dikenali oleh kompiler, dan hanya dapat digunakan untuk menginisialisasi objek yang mendukung protokol "ExpressibleByBooleanLiteral". Bool memiliki variabel internal "_value" yang mengandung integer satu bit.

Jadi Bool bukan bagian dari bahasa Swift, tetapi dari perpustakaan standar. benar dan salah adalah bagian dari bahasa, dan begitu juga protokol ExpressibleByBooleanLiteral.

gnasher729
sumber