2013-04-23 16 views
25

Say, bazı nesnelerle özel bir List döndüren bir yöntemim var. Bana Object olarak döndüler. Bu nesnelerden belli bir alanın değerini almam gerekiyor, ama nesnelerin sınıfını bilmiyorum.Java yansıması: Bir nesneden alan değeri elde etme, sınıfını bilmeyen

Bunu Reflecion veya başka şekilde yapmak için bir yolu var mı?

+8

'Object.getClass()' - http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Object.html#getClass% 28% 29 –

cevap

38

, saha public olduğu alan, yansıma mevcut, güzel güvenlik yöneticisi).

List<Foo> list; //From your method 
for(Foo foo:list) { 
    Object fieldValue = foo.fieldName; 
} 

Yoksa jenerik 'değiller bir Java 1.4 arayüzünü tüketen eğer: Bir List<Foo> dönmek yöntemini değiştirmek durumunda yineleyici o zaman bilgileri yazın verebilir çünkü

, bu çok kolay hale geldi mevcut t, ancak listede olmalı nesnelerin türünü bilmek ...

List list; 
for(Object x: list) { 
    if(x instanceof Foo) { 
     Object fieldValue = ((Foo)x).fieldName; 
    } 
} 

yok yansıması gerekli :)

+0

_ "Varsayımlar ... yansıma mevcut (android değil)" _ Kim kime göre? 'Yansıtma' paketi, API lvl 1 http://developer.android.com/reference/java/lang/reflect/package-summary.html –

+0

'dan beri Android'de kullanıma sunulmuştur. Görünüşe göre bir yıl önce bu küçük noktada yanıldım. Düzenlenen. Ana nokta kalır, yansıma bir sebep veya başka bir nedenle mevcut olmayan birçok Java tabanlı platform vardır. – Charlie

+0

@Charlie: Ancak, SQL sorgularından gelen Nesnelerle nasıl baş edileceği Hazırda bekletildi. Birden çok yönteme sahipseniz, her sonuç için bu kadar çok sayıda sınıf prototipini oluşturmanız gerekir. Bunun için daha kolay bir yolu var mı? –

4

Alanın hangi sınıfa ait olduğunu biliyorsanız yansıma kullanarak ona erişebilirsiniz. Bu örnek (Groovy'de fakat yöntem çağrıları aynıdır) Foo sınıfı için bir Field nesnesi alır ve b nesnesinin değerini alır. Bu, nesnenin kesin somut sınıfını önemsemenize gerek olmadığını gösterir, önemli olan, alanın açık olduğu sınıfı bildiğiniz ve bu sınıfın ya somut sınıf ya da nesnenin bir üst sınıfı olduğudur.

List list; // from your method 
for(Object x : list) { 
    Class<?> clazz = x.getClass(); 
    Field field = clazz.getField("fieldName"); //Note, this can throw an exception if the field doesn't exist. 
    Object fieldValue = field.get(x); 
} 

Ama bu çok çirkin olduğunu ve deneme-yakalar tüm dışarı bıraktı ve varsayımlar bir dizi (kamu yapar: Basit bir dava varsayarsak

groovy:000> class Foo { def stuff = "asdf"} 
===> true 
groovy:000> class Bar extends Foo {} 
===> true 
groovy:000> b = new Bar() 
===> [email protected] 
groovy:000> f = Foo.class.getDeclaredField('stuff') 
===> private java.lang.Object Foo.stuff 
groovy:000> f.getClass() 
===> class java.lang.reflect.Field 
groovy:000> f.setAccessible(true) 
===> null 
groovy:000> f.get(b) 
===> asdf 
+0

Kuşkusuz, eğer groovy kullanıyorsanız, en son kullandığım zaman (~ 1.7 dönemi), sadece sizin için özel bir şey yapıp ... ayrıca java fasülyesi yöntemleri için gettatik şeker yapar getX). :) – Charlie

+0

@Charlie: evet, 'b.stuff' bu groovy için bunun için yeterlidir. –

+0

Bu harika çalışıyor ve kullanım durumumun büyük anahtarı setAccessible kullanıyordu; Bu zayıf bir Java geçmişinden geliyor, ancak diğer dillerde daha güçlü. Bazı yansıma mantığını taşıdım ve setAccessible eklenene kadar değerlere erişemediğim şeyler başarısız oldu. – Hazok

1

Ben stron gly, o listede hangi nesnenin bulunduğunu belirtmek için Java jeneriklerini kullanmanızı önerir. List<Car>. Otomobil ve Kamyonlarınız varsa, bu List<Vehicle> gibi bir ortak süper sınıf/arayüz kullanabilirsiniz.

Ancak, aşağıda çalıştırılabilir örnekte olduğu gibi özel olsalar bile alanlar erişilebilir hale getirmek Spring'in ReflectionUtils kullanabilirsiniz:

List<Object> list = new ArrayList<Object>(); 

list.add("some value"); 
list.add(3); 

for(Object obj : list) 
{ 
    Class<?> clazz = obj.getClass(); 

    Field field = org.springframework.util.ReflectionUtils.findField(clazz, "value"); 
    org.springframework.util.ReflectionUtils.makeAccessible(field); 

    System.out.println("value=" + field.get(obj)); 
} 

Running bunun bir çıkışı vardır:

değeri = [ C @ 1b67f74
değeri = 3

+0

Generics durum böyle değil, çünkü yöntem koduna erişimim yok. – svz

+0

Gönderdiğim örnek o zaman yardımcı olacaktır. Çalışma zamanı denetimi için özel ve son alanların bile erişilebilir olmasını sağlar. – Erich

+0

"[C @ 1b67f74" yerine "bir değer" değerini nasıl alabilirim? –

0

, benim yanlısı aynı durum var bir yol daha vardır geçebilirler. i biliyorum bu şekilde yukarıdaki hazırda sorgu dilinde

List<Object[]> list = HQL.list();

çözüldü yaptım yani ne zaman nesnelerin ne olduğu yerde yer almaktadır:

for(Object[] obj : list){ 
String val = String.valueOf(obj[1]); 
int code =Integer.parseint(String.valueof(obj[0])); 
} 

Birlikte karışık nesneler alabilirsiniz bu şekilde Kolaylık, ama önceden hangi değeri aldığınızı veya bilmeniz gereken değerleri yazdırarak kontrol edebilmeniz gerekir. Ben O getiriliyor alanları ve dizi öğelerini, örneğin destekler

1
public abstract class Refl { 
    /** Use: Refl.<TargetClass>get(myObject,"x.y[0].z"); */ 
    public static<T> T get(Object obj, String fieldPath) { 
     return (T) getValue(obj, fieldPath); 
    } 
    public static Object getValue(Object obj, String fieldPath) { 
     String[] fieldNames = fieldPath.split("[\\.\\[\\]]"); 
     String success = ""; 
     Object res = obj; 
     for (String fieldName : fieldNames) { 
      if (fieldName.isEmpty()) continue; 
      int index = toIndex(fieldName); 
      if (index >= 0) { 
       try { 
        res = ((Object[])res)[index]; 
       } catch (ClassCastException cce) { 
        throw new RuntimeException("cannot cast "+res.getClass()+" object "+res+" to array, path:"+success, cce); 
       } catch (IndexOutOfBoundsException iobe) { 
        throw new RuntimeException("bad index "+index+", array size "+((Object[])res).length +" object "+res +", path:"+success, iobe); 
       } 
      } else { 
       Field field = getField(res.getClass(), fieldName); 
       field.setAccessible(true); 
       try { 
        res = field.get(res); 
       } catch (Exception ee) { 
        throw new RuntimeException("cannot get value of ["+fieldName+"] from "+res.getClass()+" object "+res +", path:"+success, ee); 
       } 
      } 
      success += fieldName + "."; 
     } 
     return res; 
    } 

    public static Field getField(Class<?> clazz, String fieldName) { 
     Class<?> tmpClass = clazz; 
     do { 
      try { 
       Field f = tmpClass.getDeclaredField(fieldName); 
       return f; 
      } catch (NoSuchFieldException e) { 
       tmpClass = tmpClass.getSuperclass(); 
      } 
     } while (tmpClass != null); 

     throw new RuntimeException("Field '" + fieldName + "' not found in class " + clazz); 
    } 

    private static int toIndex(String s) { 
     int res = -1; 
     if (s != null && s.length() > 0 && Character.isDigit(s.charAt(0))) { 
      try { 
       res = Integer.parseInt(s); 
       if (res < 0) { 
        res = -1; 
       } 
      } catch (Throwable t) { 
       res = -1; 
      } 
     } 
     return res; 
    } 
} 

bu yardım umut kötü İngiliz için üzgün:

noktalar ve parantez arasında hiçbir fark yoktur
System.out.println(""+Refl.getValue(b,"x.q[0].z.y")); 

, sadece sınırlayıcı olarak kullanılır ve boş alan adları göz ardı edilir:

System.out.println(""+Refl.getValue(b,"x.q[0].z.y[value]")); 
System.out.println(""+Refl.getValue(b,"x.q.1.y.z.value")); 
System.out.println(""+Refl.getValue(b,"x[q.1]y]z[value"));