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.