Бульський клас Java - чому б не перерахувати?


11

Мені здається, що булевий клас - ідеальний кандидат, який можна реалізувати як ентум.

Дивлячись на вихідний код, більшість класів - це статичні методи, які можна перемістити без змін до перерахунку, решта стають набагато простішими як перерахунки. Порівняйте оригінал (коментарі та статичні методи видалено):

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

з переліченою версією:

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

Чи є якась причина, чому Бул не міг стати перелюбом?

Якщо це код 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 не повинен мати більше двох значень. Для Java це було б безглуздо, оскільки він має спеціалізовану заяву для обробки булевих ( if), але з точки зору концептуальної / типової точки зору булеві та перерахунки - це обидва типи типів суми, тому я думаю, що справедливо запитати, чому вони не Не подолайте розрив між ними.
Доваль

1
Примітка. Ви, здається, також пропустили реалізацію valueOf(String)(що суперечило б значенню enf) і магію, за getBooleanякою це може зробити так, що Boolean.valueOf("yes")повертає справжнє, а не хибне. Обидві вони є частиною 1,0 специфікації та потребують відповідної сумісності ззаду.

Відповіді:


13

Ну, мабуть, я міг би почати з аргументу, що перерахування Java не були додані до мови програмування Java до JDK 1.5. і тому це рішення не було навіть альтернативою в перші дні, коли визначався булевий клас.

При цьому, Java має репутацію зберігати зворотну сумісність між випусками, і тому, навіть якщо ми сьогодні можемо вважати ваше рішення хорошою альтернативою, ми не можемо це зробити, не порушивши тисячі рядків коду там, використовуючи вже старий булевий клас.


3
Можливо, вам допоможе специфікація мови Java 1.0 для java.lang.Boolean. new Boolean("True")І new Boolean("true")також може викликати деякі проблеми , пов'язані з гіпотетичної реалізації перерахувань.

Допускається дозволити декілька об'єктів (незмінних), тому використання наданих конструкторів на Boolean не є гарною ідеєю - як йдеться в документації API.
Хайленд Марк

Специфікація мови не допоможе у подібному питанні, оскільки не визначає реалізацію Класів. Ось як найкраще реалізувати специфікацію.
Хайленд Марк

13

Є деякі речі, які не працюють і не спрацьовують досить дивно, коли ви порівнюєте їх з попередніми функціональними можливостями Java.

Ми будемо ігнорувати бокс, оскільки це було додано з 1.5. Гіпотетично, якби Сунь захотів, вони могли б змусити enum Booleanсебе вести себе так само, як бокс, що виступав на "Бюро" class Boolean.

Тим не менш, є й інші дивні (для кодера) способи, що це раптом зламається порівняно з функціональністю попереднього класу.

Проблема 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: Немає постійної перерахунку BooleanStuff.MyBoolean.FaLsE
    на java.lang.Enum.valueOf (Enum.java:236)
    на BooleanStuff $ MyBoolean.valueOf (BooleanStuff.java:17)
    на BooleanStuff.main (BooleanStuff.java:13)

Проблема тут полягає в тому, що я не можу пройти через все, що немає TRUEабо не FALSEпотрібно valueOf(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);
}

Ми повертаємо нашу помилку виконання:

правда
Виняток у потоці "main" java.lang.IllegalArgumentException: Немає постійної перерахунку BooleanStuff.MyBoolean.false
    на java.lang.Enum.valueOf (Enum.java:236)
    на BooleanStuff $ MyBoolean.valueOf (BooleanStuff.java:11)
    на BooleanStuff.main (BooleanStuff.java:7)

Чому хтось це писав би? Данно ... але його код, який раніше працював і більше не працював.


Не зрозумійте мене неправильно, мені дуже подобається ідея лише однієї копії даного незмінного об'єкта ніколи. Перелік вирішує цю проблему. Я особисто стикався з кодом постачальника, у якому були помилки в коді постачальника, який виглядав приблизно так:

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

це ніколи не працювало (Ні, я не виправляв це, тому що неправильний стан був виправлений десь в іншому місці, і виправлення цього зламало, що дивними способами, що я дійсно не мав часу налагоджувати) . Якби це був перерахунок, цей код працював би замість цього.

Однак необхідність синтаксису навколо enum (з урахуванням регістру - копатися в enumConstantDirectory позаду valueOf, помилки виконання, які потрібно працювати таким чином для інших переліків) та спосіб роботи статичних методів призводить до порушення ряду речей, що заважає йому будучи краплею заміни булева.


1
Якщо хтось проектував, з нуля нову мову з знаннями про те, як працює Java (і ні), тип булевого об'єкта є структурою як перелік ... це просто не зовсім відповідає тому, як працює Java. Я впевнений, що деякі мовні дизайнери збиваються з цього приводу. Якщо можна почати з Java 8 і такі речі, як методи за замовчуванням в інтерфейсах, я впевнений, що багато неправильних можливостей Java могло бути зроблено трохи чистіше - в той же час, я дійсно вдячний за можливість взяти якийсь код Java 1.3 і досі компілюється в 1.8 - і ось де ми зараз.

Не було б дуже складно додати ofабо fromметод і відповідний javadoc.
assylias

@assylias конвенція з більшою частиною іншого коду Java є, valueOfі Boolean.valueOf () існує там з 1.0 . Або Enums не зможе використовувати valueOf як статичний метод, або Булеві знадобиться інший метод, ніж той, який він використовує. Якщо порушити умовність або сумісність, - і не мати Булевим бути перерахунком, це також не порушує. З цього вибір досить простий.

"Але ... тут є проблема. Ви не можете перекрити статичний метод." Ви нічого не "переосмислюєте" - метод все одно не існує в суперкласі. Проблема натомість полягає в тому, що метод автоматично визначається для всіх переліків, і ви не можете його переосмислити.
користувач102008

"Для 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, яке також не працює з перерахунком.

@MichaelT Я думаю, що компілятор все-таки зможе розблокувати його та зробити ifроботу так само, як це робить зараз. З іншого боку, немає ніякого способу ігнорувати той факт, що ви додали до Booleanцього додаткової функціональності boolean.
Довал

Whoa ... ти не можеш писати заяви на переключення для булів у Java? Це божевільно.
Томас Едінг

Класи боксу діють лише як примітиви через розпакування. У Integer немає + оператора.
Хайленд Марк

@HighlandMark Це правда, але моя думка полягає в тому, що вони переживали великі болі, щоб переконатися, що види боксу використовувались так само, як і їх примітивні аналоги. Unboxing - це те, що їм довелося реалізувати, але це не безкоштовно.
Довал

0

Крім valueOfвипуску (який є проблемою на рівні Java, він може чудово працювати на рівні JVM), це тому, що Booleanмає публічний конструктор. Це була погана ідея, на сьогоднішній день застаріла, але тут залишається одна.


0

Причина в тому, що "bool" був частиною мови Java набагато раніше, ніж "enum". Протягом багатьох років "bool" було дуже бажано мати, тоді як "enum" був недоступний. Тільки тепер ви можете сказати, "якби enum був доступний з самого початку, то ми могли б реалізувати bool як enum замість окремого типу".

У Swift, який міг би виражати "bool" як перерахунок, є три структури під назвою "Bool", "DarwinBoolean" та "ObjCBool", що реалізують протокол "ExpressibleByBooleanLiteral". (DarwinBoolean сумісний з C або C ++ bool, ObjCBool ​​сумісний з BOOL Objective-C). "true" та "false" - це спеціальні значення, визнані компілятором, і їх можна використовувати лише для ініціалізації об'єктів, що підтримують протокол "ExpressibleByBooleanLiteral". Bool має внутрішню змінну "_value", що містить ціле число біт.

Тож Bool не є частиною мови Swift, а стандартною бібліотекою. true та false є частиною мови, як і протокол ExpressibleByBooleanLiteral.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.