Java anahtarı deyimi: Sabit ifade gerekli, ancak sabit


175

Yani, birkaç statik sabitleri olan bu sınıf üzerinde çalışıyorum:

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

Sonra, sabit dayalı ilgili bir dize almak için bir yol istiyorum:

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

Ancak, derlediğimde, constant expression required3 vaka etiketlerinin her birinde bir hata alıyorum.

Derleyicinin bir anahtarı derlemek için derleme zamanında bilinmesi gereken ifadeye ihtiyacı olduğunu anlıyorum, ama neden Foo.BA_sabit değil ?


1
Bu durumda bir enum kullanmamak için herhangi bir neden var mı?
barrowc

1
Java'nın sıralaması olduğunu düşünmemiştim. public static final intJDK boyunca dağılmışlar, işte bu yüzden gittim.
Austin Hyde


4
Ve Etkili Java'yı ( java.sun.com/docs/books/effective ) okuyun , Madde 30: int sabitleri yerine numaralandırma kullanın
Sean Patrick Floyd

İpuçları çocuklar için teşekkürler, ben kontrol edeceğim.
Austin Hyde

Yanıtlar:


150

Derleyici bir anahtar derlemek için derleme zamanında bilinmesi için ifade ihtiyacı anlıyorum, ama neden Foo.BA_ sabit değil?

Alanlar başlatıldıktan sonra yürütülen herhangi bir kodun perspektifinden sabit olmalarına rağmen , JLS'nin gerektirdiği anlamda bir derleme zaman sabiti değildir ; Sabit bir ifadenin özellikleri için bkz. §15.28 Sabit İfadeler 1 . Bu , aşağıdaki gibi bir "sabit değişken" tanımlayan §4.12.4 Son Değişkenleri ifade eder :

İlkel tür veya String türünde, derleme zamanı sabit ifadesiyle (§15.28) sabit bir değişkenle sonlandırılmış ve başlatılmış bir değişken diyoruz. Bir değişkenin sabit bir değişken olup olmadığı, sınıf başlatma (§12.4.1), ikili uyumluluk (§13.1, §13.4.9) ve kesin ödev (§16) ile ilgili sonuçlar doğurabilir.

Örneğin, Foo.BA * değişkenlerinin başlatıcıları yoktur ve bu nedenle "sabit değişkenler" olarak nitelendirilmezler. Düzeltme basit; Foo.BA * değişken bildirimlerini derleme zamanı sabit ifadeleri olan başlatıcılar olacak şekilde değiştirin.

Diğer örneklerde (başlatıcıların zaten derleme zamanı sabit ifadeleri olduğu durumlarda), değişkeni finalgereken gibi bildirme .

Kodunuzu sabitlerden enumziyade kullanmak için değiştirebilirsiniz int, ancak bu başka birkaç kısıtlama getirir:


1 - Sabit ifade kısıtlamaları aşağıdaki gibi özetlenebilir. Bir sabit ifadeler) ilkel türde kullanabilir ve Stringsadece, b) dışında değişmezleri (vardır primerler sağlar nullatama operatörleri dışında operatörleri izin verir) d, sabit ifadeler muhtemelen subexpressions olarak parenthesised izin verir)) ve sabit değişkenler, sadece, c ++, --ya da instanceof, ve e) tip dökümlerinin Stringsadece ilkel tiplere izin vermesi .

Not bu yöntem veya lambda çağrıları herhangi bir şekilde içermez, new, .class. .lengthveya dizi aboneliği. Ayrıca, dizi değerlerinin, enumdeğerlerin, ilkel sargı tiplerinin, boksun ve kutunun açılmasının her türlü kullanımı a) nedeniyle hariç tutulmuştur.


79

Değerleri sabitlerinizden ayırdığınız için Sabit ifadesi gerekir . Deneyin:

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

48

Android'de bu hatayı aldım ve çözümüm sadece kullanmaktı:

public static final int TAKE_PICTURE = 1;

onun yerine

public static int TAKE_PICTURE = 1;

3
Sadece açıklama için: Bu, statik bir özelliği sonlandırarak hatanızı çözer. Benim asıl sorum, nihai statik özelliğin bir başlatıcıyı eksik olması, onu sabit, ancak derleme zamanı sabiti olmamasıydı. Ayrıntılar için kabul edilen cevaba bakınız.
Austin Hyde

4
Bunun farklı bir sorun olduğunu biliyorum, ama buraya benimkinden beri aynı durumda başka birine yardım edebilir.
Teo Inke

Bu değerler çalışma zamanını değiştirebilirse işler ters gideceği için nihai olmaları mantıklıdır.
slott

31

Çünkü bunlar zaman sabiti derlemiyor. Aşağıdaki geçerli kodu göz önünde bulundurun:

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

Değerini yalnızca BARçalışma zamanında öğrenebilirsiniz .


1
İlginç. Çalışır mıydı public static final int BAR = new Random().nextInt()?
Thilo

4
Thilo'nun ifadesi derlenir, ancak switch ifadesi gereken sabit ifadeden şikayet eder . Ayrıca, iki ardışık new Random().nextInt()aynı değerleri döndüremez mi?
Tony Ennis

2
@Tony: Bu iyi bir şey. Derleme yapmaz, çünkü derleme zamanı sabiti ile başlatılmaz. Stephen'ın kabul ettiği cevaba bakınız. Eğer bu derlenirse, rasgele bir tamsayı sınıfa zor kodlanırdı ve oldukça tahmin edilemez sonuçlar elde edilir.
Thilo

Anahtardaki sabitin reddedildiğine şaşırdım ve 'sabit' kendisi değil. Asla bu şekilde olacağını düşünmemiştim. Tabii ki, sanırım gerçekten bir sabit değil.
Tony Ennis

@TonyEnnis - Gerçekten sabit olarak ne demek istediğinize bağlı. Programın yürütülmesi sırasında değişmeyeceği anlamında gerçekten sabittir (modulo birkaç kelime oyunu). Ancak tüm infazlar için aynı değildir.
Stephen C

17

Bu örnekteki gibi bir numaralandırma kullanabilirsiniz:

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

Kaynak: Enum ile Switch deyimi


Merhaba, hala sayımı bu şekilde kullanmada sorun yaşıyorum: <br/> enum Codes { CODE_A(1), CODE_B(2); private mCode; Codes(int i) { mCode = i; } public int code() { return mCode; } }<br/> Anahtardaki sayımı kullanmaya çalıştığımda aynı hatayı alıyorum ... <br/> switch(field) { case Codes.CODE_A.code() : // do stuffs.. ; } <br/> Sorunu çözmek mümkün mü?
shaolin

1
@stiga - Yalnızca numaralandırma örneklerini kendileri açabilirsiniz. Enum örneklerinde bir yöntem çağrılarak döndürülen bir değerde değil.
Stephen C

3

Bu, yıllar önce cevaplandı ve muhtemelen ilgili değil, ama her ihtimale karşı. Bu sorunla karşılaştığımda ifbunun yerine bir ifade kullandım switch, hatayı çözdü. Tabii ki bir çözüm ve muhtemelen "doğru" çözüm değil, ama benim durumumda bu yeterliydi.


4
Bu geçici bir çözümdür ve sorunun cevabı değildir
J. Doe

Neden oyları düşürüyorum? meşru bir çözüm
Samer Murad

2
muhtemelen bir IF deyimi özellikle bir anahtar ile kaçınmaya çalışıyorum çünkü
Dean Wild

1
Aşağıya oy verdim çünkü buradaki soru problemi "nasıl" çözmek değil, "neden" sorun meydana geldi. Bence cevabınız bağlam dışı. Ayrıca mükemmeliyetçiyseniz, switchbunun genellikle uzun olandan daha hızlı olduğunu fark etmelisiniz if-else, çünkü switchkoşulu sadece bir kez kontrol edin , oysa doğru olanı bulmadan önce tüm durumu if-elsekontrol etmeniz gerekebilir .
Christian Lim

0

Bazen switch değişkeni de bu hatayı yapabilir:

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

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

   //...
}

Çözmek için değişkeni int olarak kullanmalısınız (bu durumda). Yani:

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

   case 0: //No Error

   //...
}

0

Böyle bir şey yaparken Android'de bu hatayı aldım:

 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

            }

bir sabit beyan etmesine rağmen:

public static final String ADMIN_CONSTANT= "Admin";

Kodumu bu şekilde değiştirerek sorunu çözdüm:

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

Benim durumumda, bu istisnayı alıyordum çünkü

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

ikinci durumda sabitin örneğini çağırıyordum var.MODIFICAR_KM:ama VariablesKmDialog.OBTENER_KMdoğrudan sınıftan kullanmalıyım.


0

Bir anahtar kasasında kullanıyorsanız, o değeri anahtara takmadan önce bile enum türünü almanız gerekir. Örneğin :

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

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

Ve numaralandırma şöyle:

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

Aşağıdaki kod kendinden açıklayıcıdır, bir anahtar kutusu ile bir numaralandırma kullanabiliriz:

/**
 *
 */
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;
    }
}

Numaralandırmaya göre sınıf değerleri eşleştirilebilir:

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

    }

Umarım yardımcı olur :)


0

Aşağıdaki yolu kullanmanızı öneririm:

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

Bence enum aşağıdaki kurucu olmalıdır: private Animal(String name) { this.name = name; }
user1364368

-1

Enums kullanmanızı tavsiye ederim :)

Şuna bir bak:

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

O zaman bu şekilde kullanabilirsiniz:

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

@djangofan kodunuzu hangi JDK sürümü üzerinde çalıştırıyorsunuz?
everton

JDK 1.7.0_74'ü IntelliJ-IDEA 14 ile kullandım
djangofan

1
Everton Agner tarafından önerilenle aynı sınıfı kullanıyorum, ancak sürekli ifadesini göstermesi gerekiyor.
Amit Kumar
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.