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 코드 인 경우, 값을 비교하기 전에 두 오브젝트의 참조를 비교하는 매우 기본적인 점검이 누락되었습니다. 이것은 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 열거 형은 두 개 이상의 값을 가질 필요가 없습니다. 부울 ( if) 처리에 대한 특수 문이 있기 때문에 Java에서는 무의미 하지만 개념 / 유형 이론 관점에서 부울과 열거 형은 모두 합계 유형의 인스턴스이므로 그 이유를 묻는 것이 공정하다고 생각합니다. 그들 사이의 간격을 메 우지 마십시오.
Doval

1
참고 : 표시는의 구현에 밖으로 놓친 valueOf(String)뒤에 (열거 형의 valueOf과 충돌)과 마술 getBoolean하는 너무 것을 할 수 있습니다 Boolean.valueOf("yes")오히려 거짓보다는 true를 돌려줍니다. 둘 다 1.0 사양의 일부이며 적절한 이전 버전과의 호환성이 필요합니다.

8
@MichaelT FileNotFound 물론입니다!
Donal Fellows

답변:


13

글쎄, JDK 1.5까지 Java 열거가 Java 프로그래밍 언어에 추가되지 않았다고 주장함으로써 시작할 수 있다고 생각 합니다. 따라서이 솔루션은 Boolean 클래스가 정의 된 초기에는 대안이 아니 었습니다.

즉, Java는 릴리스 간 이전 버전과의 호환성을 유지하는 것으로 유명하므로 오늘날 솔루션을 훌륭한 대안으로 생각하더라도 이미 이전 부울을 사용하여 수천 줄의 코드를 끊지 않으면 해결할 수 없습니다 수업.


3
당신은 찾을 수 있습니다 자바 1.0 java.lang.Boolean에 도움이 될 수 있도록 언어 사양. new Boolean("True")하고 new Boolean("true")또한 가상 열거 구현 몇 가지 문제가 발생할 수 있습니다.

API 문서에서 알 수 있듯이 여러 (불변의) 객체를 허용하는 것은 잘못된 것처럼 보이므로 부울에서 제공된 생성자를 사용하는 것은 좋지 않습니다.
Highland Mark

언어 사양은 클래스의 구현을 지정하지 않으므로 이러한 종류의 질문에는 도움이되지 않습니다. 이것이 사양을 구현하는 가장 좋은 방법입니다.
Highland Mark

13

작동하지 않는 것들이 있으며 Java 부울의 이전 기능과 비교할 때 놀라운 방법으로 작동하지 않습니다.

우리는 1.5가 추가 된 권투를 무시할 것입니다. 가설로, 만약 Sun이 원한다면 그들은에서 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이미 상자 값을 권투하는 것에 대한 경고가 나타납니다. 그러나 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);
}

런타임 오류가 다시 발생합니다.

진실
스레드 "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)에서

왜 누군가가 그것을 쓸까요? Dunno ...하지만 코드는 작동했지만 더 이상 작동하지 않습니다.


나를 잘못 이해하지 마라. 나는 주어진 불변의 객체의 사본 하나만을 좋아한다는 것을 정말로 좋아한다. 열거 형은이 문제를 해결합니다. 나는 개인적으로 다음과 같은 벤더 코드에서 버그가있는 벤더 코드를 만났다.

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

그것은 결코 작동 하지 않았습니다 (아니오, 잘못된 상태가 다른 곳에서 수정 되었기 때문에 수정하지 않았습니다.이 문제를 해결하면 실제로 디버깅 할 시간이 없었던 이상한 방식으로 파산했습니다) . 이것이 열거 형이라면 그 코드가 대신 작동했을 것입니다.

그러나 enum 주위의 구문의 필요성 (대소 문자를 구분 -enumConstantDirectory 뒤에 파고 valueOf, 다른 enum에 대해 그런 식으로 작동 해야하는 런타임 오류) 및 정적 메서드 작동 방식으로 인해 여러 가지가 깨져서 부울을 대체 할 수 있습니다.


1
Java가 작동하는 방식과 그렇지 않은 방식에 대한 지식으로 새로운 언어를 처음부터 새로 설계했다면 부울 객체 유형이 구조와 같은 열거 형이어야합니다 ... 지금은 Java가 작동하는 방식에 적합하지 않습니다. 언어 디자이너가 스스로 발로 차는 것을 확신합니다. Java 8과 인터페이스의 기본 메소드와 같은 것들로 다시 시작할 수 있다면 Java의 많은 기능이 상당히 깔끔하게 처리되었을 수 있다고 확신합니다. 일부 Java 1.3 코드를 여전히 1.8로 컴파일하면 현재 위치에 있습니다.

of또는 from메소드와 적절한 javadoc 을 추가하는 것은 그리 어렵지 않았습니다 .
assylias

@assylias는 다른 많은 Java 코드와의 규칙 valueOf이며 Boolean.valueOf ()는 1.0 이후 로 존재했습니다 . 열거 형은 valueOf를 정적 메소드로 사용할 수 없거나 부울은 사용중인 메소드와 다른 메소드가 필요합니다. 규칙이나 호환성을 위반하면 부울이 열거 형이 아니더라도 마찬가지입니다. 이것으로부터 선택은 매우 간단합니다.

"하지만 ... 여기에 문제가 있습니다. 정적 메서드를 재정의 할 수 없습니다." 당신은 아무것도 "재정의"하지 않습니다-그 방법은 어쨌든 수퍼 클래스에 존재하지 않습니다. 문제는 대신 모든 열거 형에 대해 메소드가 자동으로 정의되어 재정의 할 수 없다는 것입니다.
user102008

"foo의 경우 이미 상자 값을 boxing하는 것에 대한 경고가 표시됩니다. 그러나 bar의 코드는 구문 오류입니다."이것은 잘못된 비교입니다. 에서 Boolean.valueOf(Boolean.valueOf("TRUE")), 거기에 두 가지 valueOf 방법 : valueOf(String)valueOf(boolean). 구문 오류는 valueOf(boolean)in 구현을 잊었 기 때문에 발생합니다 MyBoolean. 다음의 언어에 하드 코딩되어 두 통화 사이의 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이하지 않습니다.
Doval

우와 ... Java에서 부울에 대한 스위치 문을 작성할 수 없습니까? 그건 미친 짓이야.
Thomas Eding

복싱 클래스는 언 박싱으로 인해 프리미티브처럼 작동합니다. 정수에는 + 연산자가 없습니다.
Highland Mark

@HighlandMark 사실이지만, 제 요점은 박스형이 기본 형식과 같은 방식으로 사용 가능하도록하기 위해 큰 고통을 겪었다는 것입니다. Unboxing은 그들이 구현해야하는 것이지 무료로 제공되지는 않습니다.
Doval

0

valueOf이슈 (Java 레벨에서 문제이며 JVM 레벨에서 잘 작동 할 수 있음) 외에도 Boolean공개 생성자가 있기 때문 입니다. 그것은 더 이상 사용되지 않는 나쁜 생각이지만, 여기에 머물러있는 아이디어입니다.


0

그 이유는 "bool"이 "enum"보다 훨씬 이전에 Java 언어의 일부이기 때문입니다. 수년 동안 "bool"은 매우 바람직한 반면 "enum"은 사용할 수 없었습니다. "지금부터 열거 형을 사용할 수 있었다면 별도의 유형 대신 부형을 열거 형으로 구현할 수있었습니다"라고 말할 수 있습니다.

"bool"을 열거 형으로 표현할 수있는 Swift에는 "ExpressibleByBooleanLiteral"프로토콜을 구현하는 "Bool", "DarwinBoolean"및 "ObjCBool"이라는 세 개의 구조체가 있습니다. 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.