Java'daki numaralandırmalarda == kullanmak uygun mudur?


111

Kullanımda için Tamam mı ==Java çeteleler üzerinde, yoksa kullanım gerekiyor .equals()? Testlerimde ==her zaman işe yarar, ancak bunun garantili olup olmadığından emin değilim. Özellikle, .clone()bir enum üzerinde bir yöntem yoktur , bu yüzden, bundan .equals()farklı bir değer döndürecek bir enum elde etmenin mümkün olup olmadığını bilmiyorum ==.

Örneğin, bu uygun mu:

public int round(RoundingMode roundingMode) {
  if(roundingMode == RoundingMode.HALF_UP) {
    //do something
  } else if (roundingMode == RoundingMode.HALF_EVEN) {
    //do something
  }
  //etc
}

Yoksa şu şekilde mi yazmam gerekir:

public int round(RoundingMode roundingMode) {
  if(roundingMode.equals(RoundingMode.HALF_UP)) {
    //do something
  } else if (roundingMode.equals(RoundingMode.HALF_EVEN)) {
    //do something
  }
  //etc
}


@assylias bu soru önce geldi. Belki ikisinin birleştirilmesi gerektiğinden emin olmadığım için ♦ dikkat için bayrak.
Matt Ball

@MattBall Bence JLS'den alıntı yapan sorunuzun cevabı en iyi cevap, bu yüzden bunu kapatmayı seçtim.
assylias

Yanıtlar:


149

Sadece benim 2 sentim: İşte Sun tarafından yayınlanan Enum.java kodu ve JDK'nın bir parçası:

public abstract class Enum<E extends Enum<E>>
    implements Comparable<E>, Serializable {

    // [...]

    /**
     * Returns true if the specified object is equal to this
     * enum constant.
     *
     * @param other the object to be compared for equality with this object.
     * @return  true if the specified object is equal to this
     *          enum constant.
     */
    public final boolean equals(Object other) { 
        return this==other;
    }


}

4
Teşekkürler! Sanırım derleyiciyle .equals () 'a adım atmayı düşünseydim bunu görürdüm ...
Kip

77

Evet, == sorun değil - her değer için yalnızca tek bir referans olması garanti edilir.

Ancak, yuvarlak yönteminizi yazmanın daha iyi bir yolu var:

public int round(RoundingMode roundingMode) {
  switch (roundingMode) {
    case HALF_UP:
       //do something
       break;
    case HALF_EVEN:
       //do something
       break;
    // etc
  }
}

Bunu yapmanın daha da iyi bir yolu, işlevselliği numaralamanın kendisine koymaktır, böylece sadece arayabilirsiniz roundingMode.round(someValue). Bu, Java numaralandırmalarının kalbine iner - başka yerlerde bulunan "adlandırılmış değerlerin" aksine, bunlar nesne yönelimli numaralandırmalar.

DÜZENLEME: Spesifikasyon çok net değil, ancak bölüm 8.9 şunu belirtiyor:

Bir enum türünün gövdesi, enum sabitleri içerebilir. Bir enum sabiti, enum türünün bir örneğini tanımlar. Bir enum türünün, enum sabitleri tarafından tanımlananlar dışında hiçbir örneği yoktur.


Bunun için sözünüze güvenmek isterim, ancak bazı resmi belgelere bir bağlantı verebilirseniz daha iyi olur ...
Kip

anahtar, farklı durumlar arasında çok fazla örtüşme olduğunda kullanışlı değildir. Ayrıca, RoundingMode java.math'ın bir parçası, bu yüzden ona bir yöntem ekleyemiyorum.
Kip

2
Oh-- ve Jon Skeet'ten şüphe mi ediyorsun? Çok uzun süredir buralarda değilsiniz;)
Joel Coehoorn

anahtar deyimlerindeki numaralar? Bunun mümkün olduğunu bilmiyordum. Bir gün denemem gerekecek.
luiscubal

Soyut yöntemler kullanarak numaralandırmalar içinde mantığı kapsüllemek, numaralandırmaların gerçek gücüdür. Kodunuzu çok daha sağlam hale getirir; Gelecekte yeni bir enum değeri eklediğinizde, derleyici sizi ilgili mantığı uygulamaya zorlayacaktır, birden çok switch deyimine bir durum eklemeyi hatırlamanız gerekmez.
Andrew Swan

13

Evet, numaralandırmadaki her değer için tekli örnekler oluşturmuşsunuz gibi:

public abstract class RoundingMode {
  public statik final RoundingMode HALF_UP = new RoundingMode ();
  public statik final RoundingMode HALF_EVEN = new RoundingMode ();

  özel RoundingMode () {
    // özel kapsam, bu sınıfın dışındaki tüm alt türleri engeller
  }
}

Bununla birlikte , enumyapı size çeşitli faydalar sağlar:

  • Her örneğin toString () kodu kodda verilen adı yazdırır.
  • (Başka bir gönderide bahsedildiği gibi), enum türündeki bir değişken, switch-casekontrol yapısı kullanılarak sabitlerle karşılaştırılabilir .
  • Numaralandırmadaki tüm değerler, valuesher bir numaralandırma türü için 'oluşturulan' alan kullanılarak sorgulanabilir
  • İşte kimlik karşılaştırmalarının en büyüğü: enum değerleri, klonlama olmadan serileştirmeden kurtulur.

Serileştirme büyük bir saçmalık. Bir sıralama yerine yukarıdaki kodu kullanacak olsaydım, kimlik eşitliği şu şekilde davranırdı:

RoundingMode original = RoundingMode.HALF_UP;
assert (RoundingMode.HALF_UP == orijinal); // geçer

ByteArrayOutputStream baos = new ByteArrayOutputStream ();
ObjectOutputStream oos = new ObjectOutputStream (baos);
oos.writeObject (orijinal);
) (Oos.flush;

ByteArrayInputStream bais = new ByteArrayInputStream (baos.toByteArray ());
ObjectInputStream ois = new ObjectInputStream (bais);
RoundingMode serisizleştirildi = (RoundingMode) ois.readObject ();

assert (RoundingMode.HALF_UP == serileştirilmemiş); // başarısız
assert (RoundingMode.HALF_EVEN == serileştirilmemiş); // başarısız

Sen edebilirsiniz içeren bir teknik kullanılarak, enum olmadan bu sorunu gidermek writeReplaceve readResolve(bkz http://java.sun.com/j2se/1.4.2/docs/api/java/io/Serializable.html ) ...

Sanırım asıl mesele şu - Java eşitliği test etmek için enum değerlerinin kimliklerini kullanmanıza izin verme yolundan çıkıyor; teşvik edilen bir uygulamadır.


1
serileştirme hatası düzeltildi. bugs.sun.com/bugdatabase/view_bug.do?bug_id=6277781
David I.

@DavidI. Güncelleme için teşekkürler. Bu çok rahatsız edici bir hata ve bilmek güzel!
Dilum Ranatunga

1
@DilumRanatunga İlk başta bunun beni etkileyeceğini düşünmüştüm, ancak bir RMI bağlantısının üzerinden geçtikten sonra sorunsuz çalışıyor gibi görünüyorlar.
David I.


6

İşte ilginç bulabileceğiniz bazı kötü kodlar. : D

public enum YesNo {YES, NO}

public static void main(String... args) throws Exception {
    Field field = Unsafe.class.getDeclaredField("theUnsafe");
    field.setAccessible(true);
    Unsafe unsafe = (Unsafe) field.get(null);
    YesNo yesNo = (YesNo) unsafe.allocateInstance(YesNo.class);

    Field name = Enum.class.getDeclaredField("name");
    name.setAccessible(true);
    name.set(yesNo, "YES");

    Field ordinal = Enum.class.getDeclaredField("ordinal");
    ordinal.setAccessible(true);
    ordinal.set(yesNo, 0);

    System.out.println("yesNo " + yesNo);
    System.out.println("YesNo.YES.name().equals(yesNo.name()) "+YesNo.YES.name().equals(yesNo.name()));
    System.out.println("YesNo.YES.ordinal() == yesNo.ordinal() "+(YesNo.YES.ordinal() == yesNo.ordinal()));
    System.out.println("YesNo.YES.equals(yesNo) "+YesNo.YES.equals(yesNo));
    System.out.println("YesNo.YES == yesNo " + (YesNo.YES == yesNo));
}

1
@Peter, lütfen bu kodun içe aktarımlarını ekleyebilir misiniz? Cound bulamadı Unsafe.class.
rumman0786

3

Numaralandırmalar, polimorfik kodu sıkıştırmak için harika bir yerdir.

enum Rounding {
  ROUND_UP {
    public int round(double n) { ...; }
  },
  ROUND_DOWN {
    public int round(double n) { ...; }
  };

  public abstract int round(double n);
}

int foo(Rounding roundMethod) {
  return roundMethod.round(someCalculation());
}

int bar() {
  return foo(Rounding.ROUND_UP);
}

1
Evet ama java.math.RoundingMode'a sahip değilim, bu yüzden bunu benim durumumda yapamam.
Kip


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.