Java 스위치 명령문 : 상수 표현식이 필요하지만 상수입니다.


175

그래서 나는 몇 가지 정적 상수를 가진이 클래스에서 일하고 있습니다 :

public abstract class Foo {
    ...
    public static final int BAR;
    public static final int BAZ;
    public static final int BAM;
    ...
}

그런 다음 상수를 기반으로 관련 문자열을 얻는 방법을 원합니다.

public static String lookup(int constant) {
    switch (constant) {
        case Foo.BAR: return "bar";
        case Foo.BAZ: return "baz";
        case Foo.BAM: return "bam";
        default: return "unknown";
    }
}

그러나 컴파일 할 때 constant expression required3 개의 사례 레이블 각각에 오류가 발생합니다.

스위치를 컴파일하려면 컴파일러가 컴파일 타임에 식을 알아야한다는 것을 알고 있지만 왜 Foo.BA_상수가 아닌가?


1
이 경우 열거 형을 사용하지 않는 이유는 무엇입니까?
barrowc

1
Java에 열거 형이 있다고 생각하지 않았습니다. public static final ints는 JDK 전체에 흩어져 있으므로 내가 함께했습니다.
Austin Hyde


4
그리고 효과적인 Java ( java.sun.com/docs/books/effective ), 항목 30 : int 상수 대신 열거 형 사용
Sean Patrick Floyd

팁 주셔서 감사합니다, 나는 그들을 확인합니다.
Austin Hyde

답변:


150

스위치를 컴파일하려면 컴파일러가 컴파일 타임에 식을 알고 있어야하지만 Foo.BA_ 상수가 아닌 이유는 무엇입니까?

필드가 초기화 된 후에 실행되는 코드의 관점에서 보면 일정하지만 JLS에 필요한 의미에서 컴파일 시간 상수 는 아닙니다 . 참조 §15.28 정수 식 (A)의 사양에 대한 상수 식 1 . 이는 다음과 같이 "일정한 변수"를 정의하는 §4.12.4 최종 변수 를 나타냅니다 .

우리는 기본 유형 또는 유형 문자열의 변수를 호출하며, 최종 변수는 컴파일 타임 상수 표현식 (§15.28) 상수 변수로 초기화됩니다. 변수가 상수 변수인지 여부는 클래스 초기화 (§12.4.1), 이진 호환성 (§13.1, §13.4.9) 및 명확한 할당 (§16)과 관련이있을 수 있습니다.

귀하의 예에서 Foo.BA * 변수에는 이니셜 라이저가 없으므로 "일정한 변수"로 규정되지 않습니다. 수정은 간단합니다. 컴파일 타임 상수 표현식 인 이니셜 라이저를 갖도록 Foo.BA * 변수 선언을 변경하십시오.

다른 예제 (이니셜 라이저가 이미 컴파일 타임 상수 표현식 인 경우)에서 final필요한 변수를 선언 합니다.

상수 enum가 아닌 int상수 를 사용하도록 코드를 변경할 수 있지만 다른 몇 가지 제한 사항이 있습니다.


1-상수 표현 제한은 다음과 같이 요약 할 수 있습니다. 일정한 표현) 기본 형식을 사용할 수 있으며, String단, B)의 간격에서 리터럴 (아르 원색 있도록 null할당 연산자 제외 연산자 가능) D, 일정한 표현이 가능 표현식으로 괄호에 있도록)) 및 상수 변수 만, C를 ++, --또는 instanceof, 및 e) 타입 캐스트를 기본 타입으로 String만 허용

참고이 방법 또는 람다 전화의 어떤 형태를 포함하지 않는 것을 new, .class. .length또는 배열 첨자. 또한 배열 값, enum값, 프리미티브 래퍼 유형의 값, 복싱 및 언 박싱의 사용은 모두 a) 때문에 제외됩니다.


79

당신은 얻을 상수 표현이 필요 당신이 당신의 상수 떨어져 값을 왼쪽 때문이다. 시험:

public abstract class Foo {
    ...
    public static final int BAR=0;
    public static final int BAZ=1;
    public static final int BAM=2;
    ...
}

48

Android 에서이 오류가 발생했으며 솔루션은 다음과 같습니다.

public static final int TAKE_PICTURE = 1;

대신에

public static int TAKE_PICTURE = 1;

3
명확히하기 위해 : 정적 속성을 최종적으로 만들어 오류를 해결합니다. 내 원래의 질문에서 문제는 최종 정적 속성에 이니셜 라이저가 누락되어 상수가되었지만 컴파일 타임 상수가 아니라는 것입니다. 자세한 내용은 허용 된 답변을 참조하십시오.
Austin Hyde

4
나는 그것이 다른 문제라는 것을 알고 있지만, 내가 여기 와서 같은 상황에서 다른 사람을 도울 수 있습니다.
Teo Inke

이러한 값이 런타임을 변경할 수 있으면 문제가 발생할 수 있으므로 최종 결과를 가져야합니다.
슬로

31

컴파일 시간 상수가 아니기 때문입니다. 다음과 같은 유효한 코드를 고려하십시오.

public static final int BAR = new Random().nextInt();

BAR런타임 의 값만 알 수 있습니다 .


1
흥미 롭군 시겠습니까 public static final int BAR = new Random().nextInt()일?
Thilo

4
Thilo의 명령문은 컴파일되지만 switch 문은 상수 표현식이 필요하다는 불평을 합니다 . 또한 두 개의 연속 new Random().nextInt()으로 동일한 값을 반환 할 수 없었 습니까?
Tony Ennis

2
@Tony : 좋은 것입니다. 컴파일 타임 상수로 초기화되지 않았으므로 컴파일되지 않습니다. 스티븐의 대답을보십시오. 이것이 컴파일되면 임의의 정수가 클래스에 하드 코딩되어 예상치 못한 결과가 발생합니다.
Thilo

스위치의 상수가 거부되고 '일정한'자체가 아니라는 것에 놀랐습니다. 나는 이것이 그렇게 될 것이라고 생각하지 않았다. 물론, 그것은 내가 생각하는 상수가 아닙니다.
Tony Ennis

@TonyEnnis-진정으로 일정하다는 의미에 달려 있습니다. 프로그램 실행 중에 변경되지 않을 것이라는 점에서 진정으로 일정합니다 (모듈로 두 번의 떨림). 그러나 모든 실행에서 동일하지는 않습니다.
Stephen C

17

이 예제와 같이 열거 형을 사용할 수 있습니다.

public class MainClass {
enum Choice { Choice1, Choice2, Choice3 }
public static void main(String[] args) {
Choice ch = Choice.Choice1;

switch(ch) {
  case Choice1:
    System.out.println("Choice1 selected");
    break;
 case Choice2:
   System.out.println("Choice2 selected");
   break;
 case Choice3:
   System.out.println("Choice3 selected");
   break;
    }
  }
}

출처 : 열거 형이있는 스위치 문


안녕하세요, 나는 여전히 이런 식으로 열거 형을 사용하는 데 문제가 있습니다 : <br/> enum Codes { CODE_A(1), CODE_B(2); private mCode; Codes(int i) { mCode = i; } public int code() { return mCode; } }<br/> 스위치에서 열거 형을 사용하려고 할 때 동일한 오류가 발생합니다 ... <br/> switch(field) { case Codes.CODE_A.code() : // do stuffs.. ; } <br/> 문제를 해결할 수 있습니까?
shaolin

1
@stiga-열거 형 인스턴스 만 켤 수 있습니다. 열거 형 인스턴스에서 메서드를 호출하여 반환 된 값이 아닙니다.
Stephen C

3

이것은 오래 전에 대답되었지만 아마도 관련이 없지만 경우에 따라 가능합니다. 이 문제에 직면했을 때 간단히 if대신 문을 사용 switch하여 오류를 해결했습니다. 물론 해결 방법이며 아마도 "올바른"해결책은 아니지만 제 경우에는 충분했습니다.


4
이것은 해결책이며 질문에 대한 답변이 아닙니다.
J. Doe

여기에서 왜 계속 투표권을 얻습니까? 합법적 인 해결 방법
Samer Murad

2
아마도 스위치를 사용하여 피하려고하는 것은 IF 문이기 때문일 것입니다.
Dean Wild

1
여기서 질문은 "어떻게"문제를 해결하는 것이 아니라 "왜"문제가 발생했기 때문에 투표했습니다. 귀하의 답변이 문맥에 맞지 않는다고 생각합니다. 또한있는 거 완벽 당신이, 당신은 그 깨달아야한다 switch일반적으로 빠른 길이보다 if-else있기 때문에, switch단지 상태를 확인 한 번 에 동안, if-else당신은 확인해야 할 수도 있습니다 모든 권리 하나를 발견하기 전에 조건을.
Christian Lim

0

때때로 스위치 변수 가 다음과 같은 오류를 일으킬 수도 있습니다.

switch(view.getTag()) {//which is an Object type

   case 0://will give compiler error that says Constant expression required

   //...
}

해결하려면 변수를 int로 캐스팅해야합니다 (이 경우). 그래서:

switch((int)view.getTag()) {//will be int

   case 0: //No Error

   //...
}

0

다음과 같은 작업을 수행하는 동안 Android에서이 오류가 발생했습니다.

 roleSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

            switch (parent.getItemAtPosition(position)) {
                case ADMIN_CONSTANT: //Threw the error

            }

상수 선언에도 불구하고 :

public static final String ADMIN_CONSTANT= "Admin";

코드를 다음과 같이 변경하여 문제를 해결했습니다.

roleSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

            String selectedItem = String.valueOf(parent.getItemAtPosition(position));
            switch (selectedItem) {
                case ADMIN_CONSTANT:

            }

0

내 경우에는이 예외가 발생했기 때문에

switch (tipoWebServ) {
                            case VariablesKmDialog.OBTENER_KM:
                                resultObtenerKm(result);
                                break;
                            case var.MODIFICAR_KM:
                                resultModificarKm(result);
                                break;
                        }

두 번째 경우 인스턴스에서 상수를 호출 var.MODIFICAR_KM:했지만 VariablesKmDialog.OBTENER_KM클래스에서 직접 사용해야합니다 .


0

스위치 케이스에서 사용하는 경우 스위치에 해당 값을 연결하기 전에 열거 형의 유형을 가져와야합니다. 예를 들면 :

SomeEnum someEnum = SomeEnum.values ​​() [1];

switch (someEnum) {
            case GRAPES:
            case BANANA: ...

그리고 열거 형은 다음과 같습니다.

public enum SomeEnum {

    GRAPES("Grapes", 0),
    BANANA("Banana", 1),

    private String typeName;
    private int typeId;

    SomeEnum(String typeName, int typeId){
        this.typeName = typeName;
        this.typeId = typeId;
    }
}

0

아래 코드는 설명이 필요하며 스위치 케이스와 함께 열거 형을 사용할 수 있습니다.

/**
 *
 */
enum ClassNames {
    STRING(String.class, String.class.getSimpleName()),
    BOOLEAN(Boolean.class, Boolean.class.getSimpleName()),
    INTEGER(Integer.class, Integer.class.getSimpleName()),
    LONG(Long.class, Long.class.getSimpleName());
    private Class typeName;
    private String simpleName;
    ClassNames(Class typeName, String simpleName){
        this.typeName = typeName;
        this.simpleName = simpleName;
    }
}

열거 형의 클래스 값을 기반으로 매핑 할 수 있습니다.

 switch (ClassNames.valueOf(clazz.getSimpleName())) {
        case STRING:
            String castValue = (String) keyValue;
            break;
        case BOOLEAN:
            break;
        case Integer:
            break;
        case LONG:
            break;
        default:
            isValid = false;

    }

그것이 도움이되기를 바랍니다 :)


0

다음과 같은 방법을 사용하는 것이 좋습니다.

public enum Animal {
    DOG("dog"), TIGER("tiger"), LION("lion");
    private final String name;

    @Override
    public String toString() {
        return this.name;
    }
}


public class DemoSwitchUsage {

     private String getAnimal(String name) {
         Animal animalName = Animal.valueOf(name);
         switch(animalName) {
         case DOG:
             // write the code required.
             break;
         case LION:
             // Write the code required.
             break;
         default:
             break;
         }
     }
}

열거 형에 다음 생성자가 있어야한다고 생각합니다. private Animal(String name) { this.name = name; }
user1364368

-1

열거 형을 사용하는 것이 좋습니다 :)

이것 좀 봐:

public enum Foo 
{
    BAR("bar"),
    BAZ("baz"),
    BAM("bam");

    private final String description;

    private Foo(String description)
    {
        this.description = description;
    }

    public String getDescription()
    {
        return description;
    }
}

그런 다음 다음과 같이 사용할 수 있습니다.

System.out.println(Foo.BAR.getDescription());

@djangofan 어떤 JDK 버전에서 코드를 실행하고 있습니까?
everton

IntelliJ-IDEA 14와 함께 JDK 1.7.0_74를 사용했습니다.
djangofan

1
Everton Agner가 제안한 것과 동일한 클래스를 사용하고 있지만 지속적인 표현이 필요합니다.
Kumar를
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.