Java에서 클래스 속성을 반복하는 방법은 무엇입니까?


85

Java의 클래스 속성을 동적으로 반복하는 방법은 무엇입니까?

예 :

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

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

이것이 자바에서 가능합니까?

답변:


100

당신이 원하는 것을하기위한 언어 적 지원은 없습니다.

리플렉션을 사용하여 런타임에 유형의 멤버에 반사적으로 액세스 할 수 있습니다 (예 : Class.getDeclaredFields()의 배열을 가져 오기 위해 with Field). 그러나 수행하려는 작업에 따라 이것이 최상의 솔루션이 아닐 수도 있습니다.

또한보십시오

관련 질문


여기에 리플렉션이 할 수있는 일 중 일부만 보여주는 간단한 예가 있습니다.

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

위의 스 니펫은 리플렉션을 사용하여 선언 된 모든 필드를 검사합니다 class String. 다음 출력을 생성합니다.

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

효과적인 Java 2nd Edition, 항목 53 : 리플렉션보다 인터페이스 선호

다음은 책에서 발췌 한 것입니다.

주어진 Class객체, 당신은 얻을 수 있습니다 Constructor, Method그리고 Field클래스의 생성자, 메소드와 필드를 나타내는 경우. [그들은] 당신이 그들의 기본 대응 물을 반사적으로 조작 할 수있게합니다 . 그러나이 힘에는 대가가 따릅니다.

  • 컴파일 타임 검사의 모든 이점을 잃게됩니다.
  • 반사 액세스를 수행하는 데 필요한 코드는 서투르고 장황합니다.
  • 성능이 저하됩니다.

일반적으로 런타임시 일반 애플리케이션에서 객체에 반사적으로 액세스해서는 안됩니다.

반영이 필요한 몇 가지 정교한 응용 프로그램이 있습니다. 예를 들어 [... 고의로 생략 됨 ...] 응용 프로그램이 이러한 범주 중 하나에 속하는지 여부에 대해 의문이 있다면 그렇지 않을 것입니다.


사실 필드의 값에 따라 각 필드의 값을 쓰고 싶습니까?
Zakaria

@Zakaria : 네, 반성으로이 작업을 수행 할 수 있지만 무엇을 하려는지에 따라 훨씬 더 나은 디자인이 있습니다.
polygenelubricants

사실, 클래스에서 생성 할 보고서가 있는데, 입력하려는 클래스 필드의 값에 따라 해당 필드를 구현할 수있는 최상의 디자인은 무엇입니까?
Zakaria

1
@Zakaria : 계속해서 반성을 시도하십시오. 거기에 Field.get당신이 반사적으로 필드의 값을 읽는 데 사용할 수있다. 만약 그렇다면 private, 당신은 setAccessible. 예, 리플렉션은 매우 강력하며 private필드 검사, 필드 덮어 쓰기 final, private생성자 호출 등과 같은 작업을 수행 할 수 있습니다 . 디자인 측면에 대한 추가 지원이 필요하면 더 많은 정보가 포함 된 새 질문을 작성해야합니다. 아마도 처음에 이러한 많은 속성이 필요하지 않을 수도 있습니다 (예 : enum또는 사용 List등).
polygenelubricants

1
제네릭은 여기서 아무 소용이 없습니다. 단지 할static void inspect(Class<?> klazz)
user102008

45

필드에 직접 액세스하는 것은 Java에서 실제로 좋은 스타일이 아닙니다. bean의 필드에 대한 getter 및 setter 메서드를 만든 다음 java.beans 패키지의 Introspector 및 BeanInfo 클래스를 사용하는 것이 좋습니다.

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

클래스가 아닌 빈의 속성 만 얻을 수있는 방법이 있습니까? getPropertyDescriptors가에 의해 반환 즉 피하기 MyBean.class
hbprotoss

@hbprotoss 내가 올바르게 이해하면을 확인 propertyDesc.getReadMethod().getDeclaringClass() != Object.class하거나 getBeanInfo(MyBean.class, Object.class). 와 같은 두 번째 매개 변수로 분석을 중지하는 클래스를 지정할 수 있습니다 .
Jörn Horstmann

20

클래스가 JavaBeabs 사양을 준수하는 경우 Jörn의 답변에 동의하지만 그렇지 않은 경우 Spring을 사용하는 경우 좋은 대안이 있습니다.

Spring에는 다음 과 같은 콜백 객체를 사용하여 클래스 필드를 반복 할 수있는 방문자 스타일 메서드 인 doWithFields (class, callback) 등 매우 강력한 기능을 제공하는 ReflectionUtils 라는 클래스 가 있습니다.

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

    });
}

그러나 여기에 경고가 있습니다. 수업은 "내부 전용"으로 표시되어 있습니다.


13

클래스 필드를 반복하고 객체에서 값을 얻는 간단한 방법 :

 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) {
      }
 }

클래스 또는 모든 객체.
Rickey

@Rickey 어쨌든 각 필드를 반복하여 속성에 새 값을 설정할 수 있습니까?
hyperfkcb

@hyperfkcb 반복하는 속성 / 필드의 값을 변경하려고 할 때 수정 예외가 발생할 수 있습니다.
Rickey

6

Java에는 Reflection (java.reflection. *)이 있지만 Apache Beanutils와 같은 라이브러리를 살펴보면 직접 리플렉션을 사용하는 것보다 프로세스가 훨씬 덜 복잡해집니다.


0

다음은 속성을 알파벳순으로 정렬하고 값과 함께 모두 인쇄하는 솔루션입니다.

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());
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.