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

ブール値が列挙型になれない理由はありますか?

これがequals()メソッドをオーバーライドするSunコードである場合、2つのオブジェクトの参照を比較してから値を比較するという非常に基本的なチェックが欠落しています。これは、equals()メソッドが次のようになるべきだと思う方法です。

   public boolean equals(Object obj) {

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

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

4
trueまたはfalseではないブール値の別の値を予測していますか?

1
@MichaelT列挙型は、2つ以上の値を持つ必要はありません。Javaには、ブール値(if)を処理するための特別なステートメントがありますが、概念/タイプ理論の観点から、ブール値と列挙型は両方とも合計タイプのインスタンスであるため、Javaでは無意味になります。それらの間のギャップを埋めないでください。
ドーバル14年

1
注:また、valueOf(String)(enumのvalueOfと競合する)の実装と、偽ではなくtrue getBooleanBoolean.valueOf("yes")返すようにすることができる魔法の実装を逃したように見えます。どちらも1.0仕様の一部であり、適切な後方互換性が必要です。

8
もちろん、@ MichaelT FileNotFound
ドナルドフェローズ14年

回答:


13

さて、Java列挙はJDK 1.5まで Javaプログラミング言語に追加されなかったと主張することから始められると思いますしたがって、このソリューションは、ブールクラスが定義された初期の代替手段でさえありませんでした。

そうは言っても、Javaはリリース間の後方互換性を維持するという評判があります。したがって、今日、あなたのソリューションを良い選択肢と考えても、すでに古いBooleanを使用して数千行のコードを壊さずにはできませんクラス。


3
あなたは見つけるかもしれないのJava 1.0 java.lang.Booleanのが助けになるために言語仕様を。new Boolean("True")そしてnew Boolean("true")また、仮想的な列挙型の実装を持ついくつかの問題が発生することがあります。

複数の(不変の)オブジェクトを許可するのは間違っているようです。そのため、提供されているConstructorをBooleanで使用することはお勧めできません(APIのドキュメントを参照)。
ハイランドマーク14年

言語仕様は、クラスの実装を指定していないため、この種の質問には役立ちません。これは、仕様を実装する最適な方法です。
ハイランドマーク14年

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

このコードの実行により、以下が得られます。

本当
本当
偽
偽
スレッド「メイン」の例外java.lang.IllegalArgumentException:列挙定数なしBooleanStuff.MyBoolean.FaLsE
    java.lang.Enum.valueOf(Enum.java:236)
    BooleanStuff $ MyBoolean.valueOf(BooleanStuff.java:17)で
    BooleanStuff.main(BooleanStuff.java:13)

ここでの問題は、私はない何を通過することができないということであるTRUEFALSEvalueOf(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は、既にボックス化された値のボックス化に関する警告が表示されます。ただし、barのコードは構文エラーです。

エラー:(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);
}

ランタイムエラーが返されます。

本当
スレッド「メイン」の例外java.lang.IllegalArgumentException:列挙定数なしBooleanStuff.MyBoolean.false
    java.lang.Enum.valueOf(Enum.java:236)
    BooleanStuff $ MyBoolean.valueOf(BooleanStuff.java:11)​​で
    BooleanStuff.main(BooleanStuff.java:7)

なぜ誰もがそれを書くのでしょうか?Dunno ...しかし、以前は機能していたコードが機能しなくなりました。


誤解しないでください。特定の不変オブジェクトのコピーを1つだけにするという考えは本当に好きです。列挙型はこの問題を解決します。私は個人的に、次のようなベンダーコードにバグがあるベンダーコードに遭遇しました。

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

それうまくいきませんでし(いいえ、間違った状態がどこかで修正されたため修正しませんでした。これを修正すると、デバッグする時間がなかった奇妙な方法でそれを壊しました)。これが列挙型の場合、そのコードは代わりに機能していました。

ただし、enumの周りの構文の必要性(大文字と小文字を区別- 背後のenumConstantDirectoryを掘り下げvalueOf、他のenumでそのように動作する必要があるランタイムエラー)と静的メソッドの動作方法により、それを防ぐ多くのことが壊れますブール型のドロップイン置換。


1
Javaがどのように動作するか(および動作しないか)を知っている新しい言語をゼロから設計し、ブールオブジェクトタイプを構造体のような列挙型にする場合... いくつかの言語設計者がそれを蹴っているはずです。Java 8とインターフェイスのデフォルトメソッドのようなものからやり直すことができれば、Javaの多くの機能がかなりきれいになったはずです-同時に、いくつかのJava 1.3コードを1.8でコンパイルします-そして、それが今の状況です。

追加することは非常に難しくなかったでしょうofか、from方法および適切なJavadocを。
assylias

@assyliasは、他のJavaコードの多くとの慣習でありvalueOf、Boolean.valueOf()は1.0から存在しています。EnumがvalueOfを静的メソッドとして使用できないか、Booleanが使用していたメソッドとは異なるメソッドが必要になります。いずれかの休憩大会や互換性を行う-とないどちらも、ブールがenum休憩も有していません。このことから、選択は非常に簡単です。

「しかし...ここには問題があります。静的メソッドをオーバーライドすることはできません。」あなたは何も「オーバーライド」していません-メソッドはとにかくスーパークラスに存在しません。問題は、代わりに、メソッドがすべての列挙型に対して自動的に定義され、再定義できないことです。
user102008

「fooの場合、既にボックス化された値のボックス化に関する警告が表示されます。ただし、barのコードは構文エラーです。」これは誤った比較です。にはBoolean.valueOf(Boolean.valueOf("TRUE"))2つの異なる valueOf方法があります:valueOf(String)valueOf(boolean)。構文エラーは、valueOf(boolean)inの実装を忘れたためですMyBoolean。その後の言語にハードコードされた2回の呼び出しの間autounboxing、ありますBooleanではなくMyBoolean、あなたが実装.IF 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現在のように動作させることができると思います。一方、あなたが持っBooleanbooleanいない追加機能を追加したという事実を無視する方法はありません。
ドーバル14年

おっ... Javaのブール値のswitchステートメントを書くことはできませんか?それはクレイジーです。
トーマスエディング14年

ボクシングクラスは、ボクシング解除のためにプリミティブのようにのみ動作します。整数には+演算子がありません。
ハイランドマーク14年

@HighlandMarkそれは本当ですが、私のポイントは、ボックス化されたタイプがそれらのプリミティブな対応物と同じ方法で使用可能であることを確認するために彼らが大きな苦労を経験したということです。箱を開けることは彼らが実装しなければならなかったもので、無料ではありません。
ドーバル14年

0

加えてvalueOfいるため、問題(Javaレベルの問題である、それはJVMレベルの罰金を仕事ができる)、それはだBooleanpublicコンストラクタを持っています。それは悪い考えであり、現在は非推奨ですが、それはここに留まるものです。


0

その理由は、「bool」は「enum」よりもずっと前にJava言語の一部であったためです。長年の間、「bool」は非常に望ましいものでしたが、「enum」は入手できませんでした。「enumが最初から使用可能であった場合、boolを別の型ではなくenumとして実装できた」と言うことができるようになりました。

「bool」を列挙として表現できたSwiftには、「ExpressibleByBooleanLiteral」プロトコルを実装する「Bool」、「DarwinBoolean」、「ObjCBool​​」という名前の3つの構造体があります。(DarwinBooleanはCまたはC ++ boolと互換性があり、ObjCBool​​はObjective-C BOOLと互換性があります)。「true」と「false」はコンパイラーによって認識される特別な値であり、「ExpressibleByBooleanLiteral」プロトコルをサポートするオブジェクトを初期化するためにのみ使用できます。Boolには、1ビット整数を含む内部変数「_value」があります。

したがって、BoolはSwift言語の一部ではなく、標準ライブラリの一部です。trueとfalseは言語の一部であり、ExpressibleByBooleanLiteralプロトコルも同様です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.