Java: Örnekleme ve Jenerikler


135

Ben bir değer dizini için genel veri yapısı bakmak önce, ben bile türünün bir örneğinin thisparametreli olup olmadığını görmek istiyorum .

Ama Eclipse bunu yaptığımda şikayet ediyor:

@Override
public int indexOf(Object arg0) {
    if (!(arg0 instanceof E)) {
        return -1;
    }

Hata mesajı:

E tipi tür parametresine karşı instanceof kontrolü gerçekleştirilemiyor. Genel tip bilgileri çalışma zamanında silineceğinden, silme Nesnesini kullanın

Bunu yapmanın daha iyi yolu nedir?

Yanıtlar:


79

Hata mesajı her şeyi söylüyor. Çalışma zamanında tür gitti, kontrol etmenin bir yolu yok.

Nesneniz için bir fabrika yaparak bunu yakalayabilirsiniz:

 public static <T> MyObject<T> createMyObject(Class<T> type) {
    return new MyObject<T>(type);
 }

Ve sonra nesnenin yapıcısında bu türü depolayın, böylece yönteminiz şöyle görünebilir:

        if (arg0 != null && !(this.type.isAssignableFrom(arg0.getClass()))
        {
            return -1;
        }

3
İstediğini sanmıyorum Class.isAssignableFrom.
Tom Hawtin - çakmak hattı

@Tom, dün gece bunu hafızadan yazdım ve aslında sınıfı geçmesi için sabitledim (duh!) Ama aksi halde neden istemeyeceğini anlamıyorum (belki bu sabah daha fazla kahveye ihtiyacım var, ' m sadece ilk fincanımda).
Yishai

Tom ile birlikteyim. Bunu açıklığa kavuşturabilir misiniz? İsAssignableFrom () yöntemini kullanmak iş için seçimim olurdu. Belki bir şey eksik?
luis.espinal

6
@luis, Tom'un yorumu muhtemelen isInstance () kullanmam ve gerçek arg0 parametresini geçmem gerektiği anlamına geliyordu. Boş denetimden kaçınma avantajı vardır.
Yishai

1
Benzer bir durumum var ama cevabı tam olarak anlayamıyorum. Benim gibi kukla için daha iyi netleştirebilir misiniz?
Rubens Mariuzzo

40

Jeneriklerle çalışma zamanı tip kontrolü için iki seçenek:

Seçenek 1 - Yapıcıyı boz

Diyelim ki indexOf (...) 'i geçersiz kıldınız ve tüm koleksiyonu yinelemek için kendinizi sadece performans için kontrol etmek istiyorsunuz.

Böyle pis bir kurucu olun:

public MyCollection<T>(Class<T> t) {

    this.t = t;
}

Ardından türü kontrol etmek için isAssignableFrom'u kullanabilirsiniz .

public int indexOf(Object o) {

    if (
        o != null &&

        !t.isAssignableFrom(o.getClass())

    ) return -1;

//...

Nesnenizi her başlattığınızda, kendinizi tekrarlamanız gerekir:

new MyCollection<Apples>(Apples.class);

Buna değmeyeceğine karar verebilirsiniz. ArrayList.indexOf (...) uygulamasında , türün eşleşip eşleşmediğini kontrol etmezler.

Seçenek 2 - Bırakalım

Bilinmeyen tipinizi gerektiren soyut bir yöntem kullanmanız gerekiyorsa, gerçekten istediğiniz tek şey derleyicinin instanceof hakkında ağlamayı bırakmasıdır . Bunun gibi bir yönteminiz varsa:

protected abstract void abstractMethod(T element);

Bu şekilde kullanabilirsiniz:

public int indexOf(Object o) {

    try {

        abstractMethod((T) o);

    } catch (ClassCastException e) {

//...

Derleyiciyi kandırmak için nesneyi T'ye (genel türünüze) döküyorsunuz. Oyuncunuz çalışma zamanında hiçbir şey yapmaz , ancak soyut yönteminize yanlış türde nesne geçirmeye çalıştığınızda yine de bir ClassCastException alırsınız.

NOT 1: Soyut yönteminizde ek denetlenmemiş dökümler yapıyorsanız, ClassCastExceptions öğeniz burada yakalanır. Bu iyi ya da kötü olabilir, bu yüzden düşünün.

NOT 2: instanceof komutunu kullanırken ücretsiz bir boş denetim alırsınız . Kullanamadığınız için çıplak elinizle null olup olmadığını kontrol etmeniz gerekebilir.


18

Eski yazı, ancak genel bir örnek yapmak için basit bir yol

public static <T> boolean isInstanceOf(Class<T> clazz, Class<T> targetClass) {
    return clazz.isInstance(targetClass);
}

21
Burada gerçekten neye katkıda bulunduğunuz belli değil.
Andrew

12

Sınıfınızın bir sınıfı genel bir parametreyle genişletmesi şartıyla, bunu çalışma zamanında yansıma yoluyla alabilir ve daha sonra karşılaştırma için kullanabilirsiniz, yani

class YourClass extends SomeOtherClass<String>
{

   private Class<?> clazz;

   public Class<?> getParameterizedClass()
   {
      if(clazz == null)
      {
         ParameterizedType pt = (ParameterizedType)this.getClass().getGenericSuperclass();
          clazz = (Class<?>)pt.getActualTypeArguments()[0];
       }
       return clazz;
    }
}

Yukarıdaki durumda, çalışma zamanında getParameterizedClass () öğesinden String.class dosyasını alırsınız ve önbelleğe alınır, böylece birden fazla kontrol üzerine herhangi bir yansıma yükü alamazsınız. ParameterizedType.getActualTypeArguments () yönteminden diğer parametrelenmiş türleri dizine göre alabileceğinizi unutmayın.


7

Aynı sorunu yaşadım ve benim çözümüm (çok mütevazı, @george: bu sefer derleme VE çalışma ...).

Benim sorunum, Observer'ı uygulayan soyut bir sınıfın içindeydi. Gözlemlenebilir yangınlar yöntemi güncellemesi (...) ile Object sınıfı olabilir ve herhangi bir Object olabilir.

Yalnızca T türündeki nesneleri işlemek istiyorum

Çözüm, çalışma zamanında türleri karşılaştırabilmek için sınıfı yapıcıya iletmektir.

public abstract class AbstractOne<T> implements Observer {

  private Class<T> tClass;
    public AbstractOne(Class<T> clazz) {
    tClass = clazz;
  }

  @Override
  public void update(Observable o, Object arg) {
    if (tClass.isInstance(arg)) {
      // Here I am, arg has the type T
      foo((T) arg);
    }
  }

  public abstract foo(T t);

}

Uygulama için sadece yapıcıya sınıfı geçmek zorundayız

public class OneImpl extends AbstractOne<Rule> {
  public OneImpl() {
    super(Rule.class);
  }

  @Override
  public void foo(Rule t){
  }
}

5

Ya da E'ye dökümde başarısız bir girişim yakalayabilirsiniz .

public int indexOf(Object arg0){
  try{
    E test=(E)arg0;
    return doStuff(test);
  }catch(ClassCastException e){
    return -1;
  }
}

1
+1 Basit bir kontrol için pragmatik bir aşırı mühendislik ürünü olmayan çözüm! Keşke daha bu oy
verdim

24
Bu işe yaramaz. E çalışma zamanında silinir, bu nedenle oyuncu başarısız olmaz, sadece derleyici uyarısı alırsınız.
Yishai

1

Teknik olarak yapmanız gerekmemelidir, bu jeneriklerin noktasıdır, böylece derleme tipi kontrol yapabilirsiniz:

public int indexOf(E arg0) {
   ...
}

ancak bir sınıf hiyerarşiniz varsa @Override bir sorun olabilir. Aksi takdirde Yishai'nin cevabına bakınız.


Evet, Liste arabirimi işlevin bir nesne parametresi almasını ister.
Nick Heiner

Liste uyguluyor musunuz? Neden <E> Listesini kullanmıyorsunuz?
Jason S

: (örneğin ArrayList bildirimi Bkz java.sun.com/j2se/1.5.0/docs/api/java/util/ArrayList.html )
Jason S

@Rosarch: Liste arabiriminin indexOf () yöntemi, verilen bağımsız değişkenin aradığınız nesneyle aynı tür olmasını gerektirmez. sadece .equals () olmalı ve farklı türdeki nesneler birbirlerine .equals () olabilir. Bu, remove () yöntemiyle aynı konudur, bkz: stackoverflow.com/questions/104799/…
newacct

1
[[[çobanca]]] boş ver, indexOf () 'in E yerine Object yerine bir parametre olduğunu düşündüm. (bunu neden yaptılar?! ??!)
Jason S

1

Nesnenin çalışma zamanı türü, filtrelemek için nispeten keyfi bir durumdur. Böyle bir salaklığı koleksiyonunuzdan uzak tutmanızı öneririm. Bu, toplama temsilcinizin bir yapıda geçirilen bir filtreye aktarılmasıyla elde edilir.

public interface FilterObject {
     boolean isAllowed(Object obj);
}

public class FilterOptimizedList<E> implements List<E> {
     private final FilterObject filter;
     ...
     public FilterOptimizedList(FilterObject filter) {
         if (filter == null) {
             throw NullPointerException();
         }
         this.filter = filter;
     }
     ...
     public int indexOf(Object obj) {
         if (!filter.isAllows(obj)) {
              return -1;
         }
         ...
     }
     ...
}

     final List<String> longStrs = new FilterOptimizedList<String>(
         new FilterObject() { public boolean isAllowed(Object obj) {
             if (obj == null) {
                 return true;
             } else if (obj instanceof String) {
                 String str = (String)str;
                 return str.length() > = 4;
             } else {
                 return false;
             }
         }}
     );

(Kullandığınız takdirde bir örnek tipi kontrolü yapmak istiyor olsa da Comparatorya da benzer.)
Tom Hawtin - tackline
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.