ProgramingTip

Java에서 제네릭 유형 인스턴스화

bestdevel 2020. 12. 29. 07:42
반응형

Java에서 제네릭 유형 인스턴스화


두건 : Java 제네릭이 작동하지 않는 이유

복제 : Java에서 일반 클래스 인스턴스화

Java에서 Generics Type의 개체를 만들고 싶습니다. 내가 어떻게 똑같이 얻을 수 있는지 제안하십시오.

참고 : 이것은 사소한 Generics 문제로 보일 수 있습니다. 하지만 .. 아니에요. :)

클래스 선언이 다음과 같다고 가정합니다.

public class Abc<T> {
    public T getInstanceOfT() {
       // I want to create an instance of T and return the same.
    }
}

public class Abc<T> {
    public T getInstanceOfT(Class<T> aClass) {
       return aClass.newInstance();
    }
}

예외 처리를 추가해야합니다.

어떤 방식으로 제공되지 않는 방법이 없습니다.


게시 한 코드에서는 T유형이 무엇인지 모르기 때문에 의 인스턴스를 만드는 것이 불가능합니다 .

public class Abc<T>
{
       public T getInstanceOfT()
       {
           // There is no way to create an instance of T here
           // since we don't know its type
       }
} 

당신에 대한 참조가있는 경우 물론이 가능 Class<T>하고 T기본 생성자 방금 전화 newInstance()Class수업입니다.

클래스 Abc<T>를 사용하면 유형 삭제 하위 문제를 사용할 수도 있고 Class<T>참조 를 필요가 없습니다 .

import java.lang.reflect.ParameterizedType;

public class Abc<T>
{
    T getInstanceOfT()
    {
        ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass();
        Class<T> type = (Class<T>) superClass.getActualTypeArguments()[0];
        try
        {
            return type.newInstance();
        }
        catch (Exception e)
        {
            // Oops, no default constructor
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args)
    {
        String instance = new SubClass().getInstanceOfT();
        System.out.println(instance.getClass());
    }
}

class SubClass
    extends Abc<String>
{
}

당신이 동의하지 않는다 . 자바의 제네릭착색 파라 메다 형성 의 기능을 추가하기 위한 것입니다.

무슨 뜻이에요? 이는 클래스의 일부 유형 변수 를 결정하지 않은 상태로 유지 하여 다양한 유형의 클래스를 사용할 수 있음을 의미합니다.

그러나 유형 변수 T 는 실행에 해결되는 속성입니다. 자바는 어떤 종류 컴파일러의 object-인지 줄 알지 않고 유형 안전성을 증명하는 클래스를 컴파일 T하므로 정적 메서드에서 유형 변수사용하는 [해석] 불가능합니다. . 형식은 개체의 실행 인스턴스에 public void static main(..)연결되고 클래스 정의에 연결되어 있고 해당 범위에서 T의미가 없습니다.

메서드 내 정적 에서 형식 변수 를 사용 하려면 메서드를 제네릭으로 선언해야 우리합니다 (이는 템플릿 클래스의 설명 된 형식 변수 가 클래스가 아닌 런타임 인스턴스 와 관련 이 있기 때문입니다 ).

class SandBox
{
  public static <T> void myMethod()
  {
     T foobar;
  }
}

이 작동하지만 main일반적인 방식으로 호출 할 수있는 방법이 없기 때문에 물론 메서드를 사용하지 않습니다 .

편집 :는 문제 유형 삭제로 인해 하나의 또는 일반 클래스가 컴파일되어 JVM에 전달된다는 것입니다. 유형 검사기는 코드가 안전한지 확인한 다음 모든 종류의 일반 정보 가 폐기 된다는 것을 증명 합니다.

하려면 인스턴스화 T의 유형을 알아야 T하지만 동시에 여러 유형이 될 수 있으므로 최소한의 리플렉션 만 필요한 솔루션 중 하나는 Class<T>새 object-를 인스턴스화하는 데 사용 하는을 구석으로입니다.

public class SandBox<T>
{
    Class<T> reference;

    SandBox(Class<T> classRef)
    {
        reference = classRef;
    }

    public T getNewInstance()
    {
        try
        {
            return reference.newInstance();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        return null;
    }

    public static void main(String[] args)
    {
        SandBox<String> t = new SandBox<String>(String.class);

        System.out.println(t.getNewInstance().getClass().getName());
    }
}

물론 이것은 인스턴스화하려는 유형을 의미합니다.

  • 기본 유형이 아닙니다.
  • 기본 생성자가 있습니다.

다른 종류의 생성자로 작동 예상 더 깊이 리플렉션해야합니다.


유형 정보를 정적으로 가져와야합니다. 이 시도 :

public class Abc<T> {
    private Class<T> clazz;

    public Abc(Class<T> clazz) {
        this.clazz = clazz;
    }

    public T getInstanceOfT()
            throws InstantiationException, IllegalAccessException {
        return clazz.newInstance();
    }
}

다음과 같이 사용하십시오.

        Abc<String> abc = new Abc<String>(String.class);
        abc.getInstanceOfT();

필요에 따라 Class<? extends T>대신 사용할 수 있습니다 .


작동하도록하는 유일한 방법은 Reified Generics 를 사용하는 것입니다 . 그리고 이것은 자바에서 지원되지 않습니다 (아직 자바 7 용 으로 계획 되었지만 연기되었습니다). 예를 들어 C #에서는 기본 생성자 T있다고 가정하여 지원됩니다 . 으로 가동 유형을 가져오고 생성 할 수도 있습니다. C #을 사용하지 않을 구문이 유효하지 않을 수 있습니다.typeof(T)Type.GetConstructor()

public class Foo<T> where T:new() {
    public void foo() {
        T t = new T();
    }
}

Java에서 이에 대한 가장 좋은 "해결 방법"은 Class<T>이미 여러 답변이 지적한 대신 메소드 인수 를 전달하는 것입니다.


우선 T, 정적이 아닌 클래스 멤버 (이 경우) 정적 정적 기본 메소드 의 형식 변수 액세스 할 수 없습니다 .

둘째, TJava가 Type Erasure로 제네릭을 구현하기 때문에 인스턴스화 할 수 없습니다 . 거의 모든 일반 정보는 시간에 지워집니다.

기본적으로 다음과 같이 할 수 없습니다.

T member = new T();

여기 제네릭 에 대한 멋진 튜토리얼이 있습니다 .


Generics의 작동 방식을 이해하지 못하는 것입니다. 은보고 할 당신 수 있습니다 http://java.sun.com/j2se/1.5.0/docs/guide/language/generics.html 기본적으로 당신이 할 수 할 [해석] 같은을 구석으로입니다

public class Abc<T>
{
  T someGenericThing;
  public Abc(){}
  public T getSomeGenericThing()
  {
     return someGenericThing;
  }

  public static void main(String[] args)
  {
     // create an instance of "Abc of String"
        Abc<String> stringAbc = new Abc<String>();
        String test = stringAbc.getSomeGenericThing();
  }

} 

다음 접근 방식을 사용하여 동일하게 구현했습니다.

public class Abc<T>
{
   T myvar;

    public T getInstance(Class<T> clazz) throws InstantiationException, IllegalAccessException
    {
        return clazz.newInstance();
    }
}

나는 더 나은 방법을 구축하고 노력했다.

가능하지 않나요?


유형 삭제 해결 방법

@martin의 답변에 영감을 받아 유형 삭제 문제를 해결할 수있는 도우미 클래스를 작성했습니다. 이 클래스 (그리고 약간 못생긴 트릭)를 사용하여 템플릿 유형에서 새 인스턴스를 만들 수 있습니다.

public abstract class C_TestClass<T > {
    T createTemplateInstance() {
        return C_GenericsHelper.createTemplateInstance( this, 0 );
    }

    public static void main( String[] args ) {
        ArrayList<String > list = 
            new C_TestClass<ArrayList<String > >(){}.createTemplateInstance();
    }
}

여기서 추악한 트릭은 클래스를 만들어 클래스 abstract의 사용자가 강제로 하위 유형을 지정하도록하는 것입니다. 여기 {}에서는 생성자에 대한 호출 뒤에 추가하여 서브 클래 싱합니다 . 이것은 새로운 익명 클래스를 정의하고 그 인스턴스를 생성합니다.

제네릭 클래스가 구체적인 템플릿 유형으로 하위 유형이 지정되면 템플릿 유형을 검색 할 수 있습니다.


public class C_GenericsHelper {
    /**
     * @param object instance of a class that is a subclass of a generic class
     * @param index index of the generic type that should be instantiated
     * @return new instance of T (created by calling the default constructor)
     * @throws RuntimeException if T has no accessible default constructor
     */
    @SuppressWarnings( "unchecked" )
    public static <T> T createTemplateInstance( Object object, int index ) {
        ParameterizedType superClass = 
            (ParameterizedType )object.getClass().getGenericSuperclass();
        Type type = superClass.getActualTypeArguments()[ index ];
        Class<T > instanceType;
        if( type instanceof ParameterizedType ) {
            instanceType = (Class<T > )( (ParameterizedType )type ).getRawType();
        }
        else {
            instanceType = (Class<T > )type;
        }
        try {
            return instanceType.newInstance();
        }
        catch( Exception e ) {
            throw new RuntimeException( e );
        }
    }
}

응용 프로그램에 대한 진입 점 역할을하는 클래스를 제네릭으로 생성하려고하는데 작동하지 않는 것 같습니다. JVM은 다음과 같이 인스턴스화 될 때 어떤 유형을 사용해야하는지 알지 못합니다. 응용 프로그램을 시작합니다.

그러나 이것이 더 일반적인 경우라면 다음과 같은 것을 찾고 있습니다.

public MyGeneric<MyChoiceOfType> getMeAGenericObject(){
   return new MyGeneric<MyChoiceOfType>();
}

또는 아마도 :

MyGeneric<String> objMyObject = new MyGeneric<String>();

정말로해야 할 때이 문제를 해결하는 방법이 있습니다.

다음은 제가 매우 유용하다고 생각하는 변환 방법의 예입니다. 제네릭의 구체적인 클래스를 결정하는 한 가지 방법을 제공합니다.

이 메서드는 개체 컬렉션을 입력으로 받아들이고 각 요소가 입력 컬렉션의 각 개체에 대해 필드 getter를 호출 한 결과 인 배열을 반환합니다. 예를 들어, a가 List<People>있고 String[]모든 사람의 성을 포함하는를 원한다고 가정합니다 .

getter가 반환하는 필드 값의 유형은 generic에 의해 지정되며 반환 값을 저장하기 위해 E유형의 배열을 인스턴스화해야 E[]합니다.

메서드 자체는 약간 못 생겼지 만이를 사용하는 코드는 훨씬 더 깔끔 할 수 있습니다.

이 기술은 입력 인수의 어딘가에 유형이 반환 유형과 일치하는 객체가있을 때만 작동하며이를 결정적으로 파악할 수 있습니다. 입력 매개 변수 (또는 하위 객체)의 구체적인 클래스가 제네릭에 대해 아무 것도 알려주지 않으면이 기술은 작동하지 않습니다.

public <E> E[] array (Collection c) {
    if (c == null) return null;
    if (c.isEmpty()) return (E[]) EMPTY_OBJECT_ARRAY;
    final List<E> collect = (List<E>) CollectionUtils.collect(c, this);
    final Class<E> elementType = (Class<E>) ReflectionUtil.getterType(c.iterator().next(), field);
    return collect.toArray((E[]) Array.newInstance(elementType, collect.size()));
}

전체 코드 : https://github.com/cobbzilla/cobbzilla-utils/blob/master/src/main/java/org/cobbzilla/util/collection/FieldTransformer.java#L28


Abc<String> abcInstance = new Abc<String> ();

.. 예 :

참조 URL : https://stackoverflow.com/questions/2434041/instantiating-generics-type-in-java

반응형