Java'da Sınıf öznitelikleri üzerinden nasıl döngü yapılır?


87

Java'da bir sınıf öznitelikleri üzerinden dinamik olarak nasıl döngü yapabilirim.

Örneğin:

public class MyClass{
    private type1 att1;
    private type2 att2;
    ...

    public void function(){
        for(var in MyClass.Attributes){
            System.out.println(var.class);
        }
    }
}

bu Java'da mümkün mü?

Yanıtlar:


101

İstediğiniz şeyi yapmak için dil desteği yok.

Çalışma zamanında yansıma kullanarak bir türün üyelerine yansıtıcı olarak erişebilirsiniz (örneğin, Class.getDeclaredFields()bir dizi elde etmek için ileField ), ancak ne yapmaya çalıştığınıza bağlı olarak, bu en iyi çözüm olmayabilir.

Ayrıca bakınız

İlgili sorular


Misal

İşte yansımanın neler yapabileceğini gösteren basit bir örnek.

import java.lang.reflect.*;

public class DumpFields {
    public static void main(String[] args) {
        inspect(String.class);
    }
    static <T> void inspect(Class<T> klazz) {
        Field[] fields = klazz.getDeclaredFields();
        System.out.printf("%d fields:%n", fields.length);
        for (Field field : fields) {
            System.out.printf("%s %s %s%n",
                Modifier.toString(field.getModifiers()),
                field.getType().getSimpleName(),
                field.getName()
            );
        }
    }
}

Yukarıdaki kod parçası, belirtilen tüm alanları incelemek için yansıma kullanır class String; aşağıdaki çıktıyı üretir:

7 fields:
private final char[] value
private final int offset
private final int count
private int hash
private static final long serialVersionUID
private static final ObjectStreamField[] serialPersistentFields
public static final Comparator CASE_INSENSITIVE_ORDER

Etkili Java 2. Sürüm, Öğe 53: Yansıtma için arayüzleri tercih edin

Bunlar kitaptan alıntılardır:

Bir Verilen Classnesne, sen alabilirsiniz Constructor, Methodve Fieldsınıfın kurucular, yöntemleri ve alanları temsil eden örnekler. [Bunlar] Eğer altta yatan meslektaşları manipüle izin düşünceli . Ancak bu gücün bir bedeli var:

  • Derleme zamanı denetiminin tüm avantajlarını kaybedersiniz.
  • Yansıtıcı erişimi gerçekleştirmek için gereken kod beceriksiz ve ayrıntılıdır.
  • Performans zarar görür.

Kural olarak, normal uygulamalarda çalışma zamanında nesnelere yansıtıcı bir şekilde erişilmemelidir.

Düşünme gerektiren birkaç karmaşık uygulama vardır. Örnekler arasında [... kasıtlı olarak ihmal edilmiştir ...] Başvurunuzun bu kategorilerden birine girip girmediğine dair herhangi bir şüpheniz varsa, muhtemelen yoktur.


aslında, alanların değerine göre, her alanın değerini yazmak istiyor muyum, yazmamak mı?
Zakaria

@Zakaria: evet, bunu derinlemesine düşünerek başarabilirsiniz, ancak ne yapmaya çalıştığınıza bağlı olarak çok daha iyi tasarımlar var.
poligenel yağlayıcılar

Aslında bir sınıftan üreteceğim bir raporum var ve bu alanlara koymak isteyip istemediğim sınıf alanlarının değerine bağlı olarak uygulayabileceğim en iyi tasarım nedir?
Zakaria

1
@Zakaria: Devam edin ve sonra düşünmeyi deneyin. Orada Field.getsen yansıtıcı bir alanın değerlerini okumak için kullanabileceğiniz. Eğer öyleyse private, bunun üstesinden gelebilirsiniz setAccessible. Evet, yansıma çok güçlüdür ve privatealanları inceleme, alanların üzerine yazma final, privatekurucuları çağırma gibi şeyler yapmanızı sağlar . Tasarım açısından daha fazla yardıma ihtiyacınız varsa, muhtemelen çok daha fazla bilgi içeren yeni bir soru yazmalısınız. Belki de ilk etapta bu kadar çok özniteliğe ihtiyacınız yoktur (örneğin kullanın enumveya Listvb.).
poligenel yağlayıcılar

1
jeneriklerin burada bir faydası yoktur. just dostatic void inspect(Class<?> klazz)
user102008

45

Alanlara doğrudan erişim java'da gerçekten iyi bir tarz değil. Bean'ınızın alanları için alıcı ve ayarlayıcı yöntemleri oluşturmanızı ve ardından java.beans paketinden Introspector ve BeanInfo sınıflarını kullanmanızı öneririm.

MyBean bean = new MyBean();
BeanInfo beanInfo = Introspector.getBeanInfo(MyBean.class);
for (PropertyDescriptor propertyDesc : beanInfo.getPropertyDescriptors()) {
    String propertyName = propertyDesc.getName();
    Object value = propertyDesc.getReadMethod().invoke(bean);
}

Sınıfın değil, yalnızca fasulyenin özelliklerini almanın bir yolu var mı? yani getPropertyDescriptors tarafından döndürülen MyBean.class'tan kaçının
hbprotoss

@hbprotoss sizi doğru anladıysam kontrol edebilir propertyDesc.getReadMethod().getDeclaringClass() != Object.classveya ikinci parametre olarak analizi durdurmak için bir sınıf belirtebilirsiniz getBeanInfo(MyBean.class, Object.class).
Jörn Horstmann

20

Jörn'ün cevabına katılıyorum JavaBeabs için sınıf uygundur spec eğer öyle değil ve bahar kullanırsanız, burada iyi bir alternatiftir.

Spring, aşağıdaki gibi bir geri arama nesnesi kullanarak sınıf alanları üzerinde yineleme yapmanızı sağlayan ziyaretçi tarzı bir yöntem olan doWithFields (sınıf, geri arama) dahil olmak üzere çok güçlü işlevler sunan ReflectionUtils adlı bir sınıfa sahiptir :

public void analyze(Object obj){
    ReflectionUtils.doWithFields(obj.getClass(), field -> {

        System.out.println("Field name: " + field.getName());
        field.setAccessible(true);
        System.out.println("Field value: "+ field.get(obj));

    });
}

Ama işte bir uyarı: sınıf "yalnızca dahili kullanım için" olarak etiketlenmiş, bana sorarsan yazık ki


14

Sınıf alanları üzerinde yineleme yapmanın ve nesneden değer elde etmenin basit yolu:

 Class<?> c = obj.getClass();
 Field[] fields = c.getDeclaredFields();
 Map<String, Object> temp = new HashMap<String, Object>();

 for( Field field : fields ){
      try {
           temp.put(field.getName().toString(), field.get(obj));
      } catch (IllegalArgumentException e1) {
      } catch (IllegalAccessException e1) {
      }
 }

Belki bir sınıf veya herhangi bir nesne.
Rickey

@Rickey Her alanı döngüye alarak özniteliklere yeni bir değer atamanın bir yolu var mı?
hyperfkcb

@hyperfkcb Üzerinde yinelediğiniz özelliklerin / alanların değerlerini değiştirmeye çalışırken bir değişiklik istisnası alabilirsiniz.
Rickey

6

Java'da Yansıma (java.reflection. *) Var, ancak Apache Beanutils gibi bir kitaplığa bakmanızı öneririm, bu işlemi doğrudan yansımayı kullanmaktan çok daha az karmaşık hale getirecektir.


0

İşte özellikleri alfabetik olarak sıralayan ve hepsini değerleriyle birlikte yazdıran bir çözüm:

public void logProperties() throws IllegalArgumentException, IllegalAccessException {
  Class<?> aClass = this.getClass();
  Field[] declaredFields = aClass.getDeclaredFields();
  Map<String, String> logEntries = new HashMap<>();

  for (Field field : declaredFields) {
    field.setAccessible(true);

    Object[] arguments = new Object[]{
      field.getName(),
      field.getType().getSimpleName(),
      String.valueOf(field.get(this))
    };

    String template = "- Property: {0} (Type: {1}, Value: {2})";
    String logMessage = System.getProperty("line.separator")
            + MessageFormat.format(template, arguments);

    logEntries.put(field.getName(), logMessage);
  }

  SortedSet<String> sortedLog = new TreeSet<>(logEntries.keySet());

  StringBuilder sb = new StringBuilder("Class properties:");

  Iterator<String> it = sortedLog.iterator();
  while (it.hasNext()) {
    String key = it.next();
    sb.append(logEntries.get(key));
  }

  System.out.println(sb.toString());
}
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.