2010-01-19 7 views
33

샘플 :Java Reflection을 사용하여 중첩 클래스의 인스턴스를 만들 수 있습니까? 코드의

public class Foo 
{ 
    public class Bar 
    { 
     public void printMesg(String body) 
     { 
      System.out.println(body); 
     } 
    } 
    public static void main(String[] args) 
    { 
     // Creating new instance of 'Bar' using Class.forname - how? 
    }   
} 

는 클래스 바의 이름을 지정의 새 인스턴스를 만들 수 있습니까? 내가 사용하려고 :

Class c = Class.forName("Foo$Bar") 

이 클래스를 발견,하지만 난 c.newInstance를 사용할 때()가있는 InstantiationException가 발생합니다.

+4

Nitpick :.. 그 중첩 클래스 아니다, 그것은 내부 구성원의 ..? (내가 제대로 JLS 용어를 기억한다면) 클래스 상자의 클래스는 정적, 쉽게 그냥 시도 메커니즘에 의해 초기화됩니다 – skaffman

+0

은'InstantiationException' –

+4

내부 클래스의 세부 사항은 무엇 이었는가 중첩 된 클래스의 유형 –

답변

54

이 작업을 수행하려면 몇 가지 작업을 수행해야합니다.

가 클래스의 지정된 public 생성자이 Class 객체가 를 표현 반영하는 Constructor 오브젝트를 돌려줍니다 첫째, 당신은 당신이 호출 할 Constructor 개체를 찾을 Class.getConstructor()를 사용해야합니다. parameterTypes 매개 변수는 의 생성자의 형식 매개 변수 유형 인 을 선언 된 순서로 식별하는 Class 개체 배열 입니다. 이 클래스 객체가 비 정적 맥락에서 선언 된 내부 클래스 을 나타내는 경우, 형식 매개 변수 유형은 첫 번째 매개 변수로 명시 적으로 둘러싸는 인스턴스를 포함한다.

그리고 당신이 Constructor.newInstance() 사용 생성자의 선언 클래스 가 아닌 정적 상황에서 내부 클래스 인 경우

는 생성자에 첫 번째 인수는 를 둘러싸해야 예를

+0

그것이 내가 필요한 것입니다. 완전한 설명에 감사드립니다! – kars7e

+0

뛰어난 설명! – Jorge

2

예. 외부 인스턴스를 내부 클래스에 공급해야한다는 것을 기억하십시오. javap을 사용하여 생성자를 찾습니다. Class.newInstance 악에 의지하기보다는 java.lang.reflect.Constructor을 통과해야합니다. 당신은 (불법으로 사용) 슈퍼 생성자를 호출하기 전에 인스턴스 필드의 할당을받을 (-target 1.4 가정 이상, 지금은 암시 적) 때문에

Compiled from "Foo.java" 
public class Foo$Bar extends java.lang.Object{ 
    final Foo this$0; 
    public Foo$Bar(Foo); 
    public void printMesg(java.lang.String); 
} 

javap -c 생성자에 대한 흥미 롭다.

public Foo$Bar(Foo); 
    Code: 
    0: aload_0 
    1: aload_1 
    2: putfield  #1; //Field this$0:LFoo; 
    5: aload_0 
    6: invokespecial #2; //Method java/lang/Object."<init>":()V 
    9: return 
+0

전에 javap에 대해 들어 본 적이 없습니다. 그 멋진 도구를 보여주는 Thx :). – kars7e

7

신속하고 더러운 코드 :

Foo.Bar.class.getConstructors()[0].newInstance(new Foo()); 

설명 : 바를 둘러싸고있는 Foo에 대해 알려야합니다.

+1

더럽지 만 짧고 잘 작동합니다. :). 고마워. 바 정적하지 않을 수 및/또는 표시되지 않을 수 있으므로 문제의 대부분을 회피한다 – kars7e

+0

... – Snicolas

+2

음, 내 대답은 클래스가 정적없는 가정? 클래스가 보이지 않는 경우 OP는 InstantiationException이 아닌 IllegalAccessException을 얻습니다. – meriton

25

먼저 내부 클래스를 생성하지 않고 내부 클래스를 생성 할 수 있습니다. 부모 클래스 외부에는 존재할 수 없습니다. 리플렉션을 할 때 부모 클래스의 인스턴스를 전달해야합니다. 중첩 된 클래스는 static이며 부모 클래스와 독립적으로 사용할 수 있으므로 리플렉션을 수행 할 때도 마찬가지입니다.

모든 내용을 보여주는 SSCCE입니다.

package mypackage; 

import java.lang.reflect.Modifier; 

public class Parent { 

    public static class Nested { 
     public Nested() { 
      System.out.println("Nested constructed"); 
     } 
    } 

    public class Inner { 
     public Inner() { 
      System.out.println("Inner constructed"); 
     } 
    } 

    public static void main(String... args) throws Exception { 
     // Construct nested class the normal way: 
     Nested nested = new Nested(); 

     // Construct inner class the normal way: 
     Inner inner = new Parent().new Inner(); 

     // Construct nested class by reflection: 
     Class.forName("mypackage.Parent$Nested").newInstance(); 

     // Construct inner class by reflection: 
     Object parent = Class.forName("mypackage.Parent").newInstance(); 
     for (Class<?> cls : parent.getClass().getDeclaredClasses()) { 
      if (!Modifier.isStatic(cls.getModifiers())) { 
       // This is an inner class. Pass the parent class in. 
       cls.getDeclaredConstructor(new Class[] { parent.getClass() }).newInstance(new Object[] { parent }); 
      } else { 
       // This is a nested class. You can also use it here as follows: 
       cls.getDeclaredConstructor(new Class[] {}).newInstance(new Object[] {}); 
      } 
     } 
    } 
} 

 
Nested constructed 
Inner constructed 
Nested constructed 
Inner constructed 
Nested constructed 
+2

우수하고 완벽한 예제! – Jorge

1

다른 답변은 당신이 원하는 무엇을 할 수있는 방법을 설명했다 생성한다.

그러나 저는 여러분에게이 작업을 수행해야한다는 사실은 시스템 설계에 약간의 잘못된 점이 있음을 보여줍니다. 나는 당신이 둘러싸는 클래스에 (정적이 아닌) 팩토리 메소드를 필요로하거나, 내부 클래스를 정적으로 선언 할 것을 제안한다.

(정적이 아닌) 내부 클래스 인스턴스를 반대로 생성하면 캡슐화가 끊어지는 "냄새"가납니다. 여기

-1

중첩 된 클래스에 대한 대답 (정적 내부) : 내 경우에는 내가

Class.forName(somePackage.innerClass$outerClass).getConstructor().newInstance(); 

은 '$가'매우 중요하다 정규화 된 이름으로 유형을 취득해야!

점으로 표시하면 클래스 "package.innerClass.outerClass"에 대해 ClassNotFoundException이 발생합니다. 예외는 :-(을 missleading있다

+0

컴파일하지 않는 없으며, 정확한'innerClass $ outerClass'의 순서는 ... –

0

이 완전히 최적은 아니지만,이 내부 클래스 및 내부 정적 클래스의 깊이 작동

public <T> T instantiateClass(final Class<T> cls) throws CustomClassLoadException { 
    try { 
     List<Class<?>> toInstantiate = new ArrayList<Class<?>>(); 
     Class<?> parent = cls; 
     while (! Modifier.isStatic(parent.getModifiers()) && parent.isMemberClass()) { 
      toInstantiate.add(parent); 
      parent = parent.getDeclaringClass(); 
     } 
     toInstantiate.add(parent); 
     Collections.reverse(toInstantiate); 
     List<Object> instantiated = new ArrayList<Object>(); 
     for (Class<?> current : toInstantiate) { 
      if (instantiated.isEmpty()) { 
       instantiated.add(current.newInstance()); 
      } else { 
       Constructor<?> c = current.getConstructor(instantiated.get(instantiated.size() - 1).getClass()); 
       instantiated.add(c.newInstance(instantiated.get(instantiated.size() - 1))); 
      } 
     } 
     return (T) instantiated.get(instantiated.size() - 1); 
    } catch (InstantiationException e) { 
     throw new CustomClassLoadException("Failed to load class.", e); 
    } catch (IllegalAccessException e) { 
     throw new CustomClassLoadException("Failed to load class.", e); 
    } catch (SecurityException e) { 
     throw new CustomClassLoadException("Failed to load class.", e); 
    } catch (NoSuchMethodException e) { 
     throw new CustomClassLoadException("Failed to load class.", e); 
    } catch (IllegalArgumentException e) { 
     throw new CustomClassLoadException("Failed to load class.", e); 
    } catch (InvocationTargetException e) { 
     throw new CustomClassLoadException("Failed to load class.", e); 
    } 
} 
관련 문제