programing

런타임에 일반 클래스 유형 가져오기

copysource 2022. 8. 10. 21:12
반응형

런타임에 일반 클래스 유형 가져오기

어떻게 하면 좋을까요?

public class GenericClass<T>
{
    public Type getMyType()
    {
        //How do I return the type of T?
    }
}

까지 해 본 것은 타입을 .Object사용되는 특정 유형이 아니라

다른 사람들이 언급했듯이, 그것은 특정한 상황에서 반성을 통해서만 가능하다.

이 타입이 꼭 필요한 경우는, 통상의(타입 세이프) 회피 패턴은 다음과 같습니다.

public class GenericClass<T> {

     private final Class<T> type;

     public GenericClass(Class<T> type) {
          this.type = type;
     }

     public Class<T> getMyType() {
         return this.type;
     }
}

이런 걸 본 적이 있어요

private Class<T> persistentClass;

public Constructor() {
    this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
                            .getGenericSuperclass()).getActualTypeArguments()[0];
 }

휴지 상태의 Generic Data Access Objects 예

제네릭은 런타임에 인증되지 않습니다.즉, 런타임에는 정보가 존재하지 않습니다.

이전 버전과의 호환성을 유지하면서 Java에 제네릭을 추가하는 것은 매우 어려운 작업입니다(과거의 미래를 안전하게 하는 것: Java 프로그래밍 언어에 일반성을 추가하는 것에 관한 중요한 문서를 참조할 수 있습니다).

이 주제에 관한 풍부한 문헌이 있고, 어떤 사람들은 현상에 대해 불만족스러워하고 있으며, 어떤 사람들은 이것이 실제로 미끼이며, 실제로 필요하지 않다고 말한다.두 링크를 모두 읽을 수 있습니다. 꽤 재미있었습니다.

Guava를 사용합니다.

import com.google.common.reflect.TypeToken;
import java.lang.reflect.Type;

public abstract class GenericClass<T> {
  private final TypeToken<T> typeToken = new TypeToken<T>(getClass()) { };
  private final Type type = typeToken.getType(); // or getRawType() to return Class<? super T>

  public Type getType() {
    return type;
  }

  public static void main(String[] args) {
    GenericClass<String> example = new GenericClass<String>() { };
    System.out.println(example.getType()); // => class java.lang.String
  }
}

얼마 전에 추상 수업과 하위 수업 등 본격적인 예를 몇 가지 올렸습니다.

주의: 이 경우 다음 서브클래스를 인스턴스화해야 합니다.GenericClass를 올바르게 수 있습니다.type correctly파 、 so so so so so so so 。 않으면 T.

Java 제네릭은 대부분 컴파일 시간이며, 이는 실행 시 유형 정보가 손실됨을 의미합니다.

class GenericCls<T>
{
    T t;
}

다음과 같은 형태로 컴파일됩니다.

class GenericCls
{
   Object o;
}

실행 시 유형 정보를 가져오려면 이를 ctor 인수로 추가해야 합니다.

class GenericCls<T>
{
     private Class<T> type;
     public GenericCls(Class<T> cls)
     {
        type= cls;
     }
     Class<T> getType(){return type;}
}

예제:

GenericCls<?> instance = new GenericCls<String>(String.class);
assert instance.getType() == String.class;

예, 그러시죠.

Java는 하위 호환성을 위해 런타임에 이 정보를 사용하지 않습니다.그러나 이 정보는 실제로 메타데이터로 존재하며 반사를 통해 액세스할 수 있습니다(그러나 여전히 형식 확인에는 사용되지 않습니다).

공식 API에서:

http://download.oracle.com/javase/6/docs/api/java/lang/reflect/ParameterizedType.html#getActualTypeArguments%28%29

하지만 당신의 시나리오에서는 반사를 사용하지 않을 것입니다.저는 개인적으로 그것을 프레임워크 코드에 사용하는 경향이 있습니다.당신의 경우 유형을 생성자 매개 변수로 추가합니다.

public abstract class AbstractDao<T>
{
    private final Class<T> persistentClass;

    public AbstractDao()
    {
        this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments()[0];
    }
}

다음과 같은 방법을 사용했습니다.

public class A<T> {

    protected Class<T> clazz;

    public A() {
        this.clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public Class<T> getClazz() {
        return clazz;
    }
}

public class B extends A<C> {
   /* ... */
    public void anything() {
       // here I may use getClazz();
    }
}

자바에서는 컴파일 할 때 type exerase를 사용하기 때문에 당신의 코드는 이전 세대의 어플리케이션이나 라이브러리와 호환성이 있습니다.

Oracle 문서:

유형 삭제

범용은 컴파일 시 보다 엄격한 유형 검사를 제공하고 범용 프로그래밍을 지원하기 위해 Java 언어에 도입되었습니다.범용 기능을 구현하기 위해 Java 컴파일러는 다음 항목에 유형 삭제를 적용합니다.

일반 유형의 모든 유형 매개 변수를 해당 경계로 바꾸거나 유형 매개 변수가 제한되지 않은 경우 개체로 바꿉니다.따라서 생성된 바이트 코드에는 일반 클래스, 인터페이스 및 메서드만 포함됩니다.필요한 경우 유형 안전을 유지하기 위해 유형 깁스를 삽입합니다.브리지 메서드를 생성하여 확장 범용 유형의 다형을 유지합니다.유형 삭제를 사용하면 매개 변수화된 유형에 대해 새 클래스가 생성되지 않으므로 일반 장치에서 런타임 오버헤드가 발생하지 않습니다.

http://docs.oracle.com/javase/tutorial/java/generics/erasure.html

이 기사에서 Ian Robertson이 설명한 기술은 나에게 효과가 있다.

간단히 말해서 빠르고 더러운 예:

 public abstract class AbstractDAO<T extends EntityInterface, U extends QueryCriteria, V>
 {
    /**
     * Method returns class implementing EntityInterface which was used in class
     * extending AbstractDAO
     *
     * @return Class<T extends EntityInterface>
     */
    public Class<T> returnedClass()
    {
        return (Class<T>) getTypeArguments(AbstractDAO.class, getClass()).get(0);
    }

    /**
     * Get the underlying class for a type, or null if the type is a variable
     * type.
     *
     * @param type the type
     * @return the underlying class
     */
    public static Class<?> getClass(Type type)
    {
        if (type instanceof Class) {
            return (Class) type;
        } else if (type instanceof ParameterizedType) {
            return getClass(((ParameterizedType) type).getRawType());
        } else if (type instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            Class<?> componentClass = getClass(componentType);
            if (componentClass != null) {
                return Array.newInstance(componentClass, 0).getClass();
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * Get the actual type arguments a child class has used to extend a generic
     * base class.
     *
     * @param baseClass the base class
     * @param childClass the child class
     * @return a list of the raw classes for the actual type arguments.
     */
    public static <T> List<Class<?>> getTypeArguments(
            Class<T> baseClass, Class<? extends T> childClass)
    {
        Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
        Type type = childClass;
        // start walking up the inheritance hierarchy until we hit baseClass
        while (!getClass(type).equals(baseClass)) {
            if (type instanceof Class) {
                // there is no useful information for us in raw types, so just keep going.
                type = ((Class) type).getGenericSuperclass();
            } else {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                Class<?> rawType = (Class) parameterizedType.getRawType();

                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
                for (int i = 0; i < actualTypeArguments.length; i++) {
                    resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
                }

                if (!rawType.equals(baseClass)) {
                    type = rawType.getGenericSuperclass();
                }
            }
        }

        // finally, for each actual type argument provided to baseClass, determine (if possible)
        // the raw class for that type argument.
        Type[] actualTypeArguments;
        if (type instanceof Class) {
            actualTypeArguments = ((Class) type).getTypeParameters();
        } else {
            actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
        }
        List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
        // resolve types by chasing down type variables.
        for (Type baseType : actualTypeArguments) {
            while (resolvedTypes.containsKey(baseType)) {
                baseType = resolvedTypes.get(baseType);
            }
            typeArgumentsAsClasses.add(getClass(baseType));
        }
        return typeArgumentsAsClasses;
    }
  }

나는 또 다른 우아한 해결책이 있다고 생각한다.

일반 유형 매개 변수의 유형을 (안전하게) 구체 클래스에서 슈퍼 클래스로 "전달"하는 것이 좋습니다.

클래스 유형을 클래스의 "메타데이터"로 생각할 수 있는 경우, 이는 런타임에 메타데이터를 인코딩하는 Java 메서드를 제안합니다: 주석.

먼저 다음 선을 따라 사용자 지정 주석을 정의합니다.

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityAnnotation {
    Class entityClass();
}

그런 다음 주석을 하위 클래스에 추가해야 합니다.

@EntityAnnotation(entityClass =  PassedGenericType.class)
public class Subclass<PassedGenericType> {...}

그런 다음 이 코드를 사용하여 기본 클래스의 클래스 유형을 가져올 수 있습니다.

import org.springframework.core.annotation.AnnotationUtils;
.
.
.

private Class getGenericParameterType() {
    final Class aClass = this.getClass();
    EntityAnnotation ne = 
         AnnotationUtils.findAnnotation(aClass, EntityAnnotation.class);

    return ne.entityClass();
}

이 접근법에는 다음과 같은 제한이 있습니다.

  1. 타입을 합니다.PassedGenericType두). ()) )( ) )( ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) 。
  2. 이것은 콘크리트 하위 클래스를 수정할 수 있는 경우에만 가능합니다.

제 솔루션은 다음과 같습니다.

import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;

public class GenericClass<T extends String> {

  public static void main(String[] args) {
     for (TypeVariable typeParam : GenericClass.class.getTypeParameters()) {
      System.out.println(typeParam.getName());
      for (Type bound : typeParam.getBounds()) {
         System.out.println(bound);
      }
    }
  }
}

한 가지 방법은 다음과 같습니다. 한두 번 사용해 본 적이 있습니다.

public abstract class GenericClass<T>{
    public abstract Class<T> getMyType();
}

와 함께

public class SpecificClass extends GenericClass<String>{

    @Override
    public Class<String> getMyType(){
        return String.class;
    }
}

이 택시를 위한 간단한 솔루션은 다음과 같습니다.

public class GenericDemo<T>{
    private T type;

    GenericDemo(T t)
    {
        this.type = t;
    }

    public String getType()
    {
        return this.type.getClass().getName();
    }

    public static void main(String[] args)
    {
        GenericDemo<Integer> obj = new  GenericDemo<Integer>(5);
        System.out.println("Type: "+ obj.getType());
    }
}

그럴수는 없어요.타입 T의 멤버 변수를 클래스에 추가하면(초기화할 필요도 없음) 이 변수를 사용하여 유형을 복구할 수 있습니다.

여기 유효한 솔루션이 있습니다!!!

@SuppressWarnings("unchecked")
    private Class<T> getGenericTypeClass() {
        try {
            String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();
            Class<?> clazz = Class.forName(className);
            return (Class<T>) clazz;
        } catch (Exception e) {
            throw new IllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");
        }
    } 

참고: 슈퍼클래스로만 사용할 수 있습니다.
1. (1. 타입 클래스로.Child extends Generic<Integer>) 또는
실장으로 (2. 익명으로 작성되어야 .new Generic<Integer>() {};

여기서 몇 가지 답을 완성하기 위해 파라미터화된계층이 얼마나 높은지에 관계없이 재귀의 도움을 받아 MyGeneric Class 유형:

private Class<T> getGenericTypeClass() {
        return (Class<T>) (getParametrizedType(getClass())).getActualTypeArguments()[0];
}

private static ParameterizedType getParametrizedType(Class clazz){
    if(clazz.getSuperclass().equals(MyGenericClass.class)){ // check that we are at the top of the hierarchy
        return (ParameterizedType) clazz.getGenericSuperclass();
    } else {
        return getParametrizedType(clazz.getSuperclass());
    }
}

이것이 나의 해결책이다.

public class GenericClass<T>
{
    private Class<T> realType;

    public GenericClass() {
        findTypeArguments(getClass());
    }

    private void findTypeArguments(Type t) {
        if (t instanceof ParameterizedType) {
            Type[] typeArgs = ((ParameterizedType) t).getActualTypeArguments();
            realType = (Class<T>) typeArgs[0];
        } else {
            Class c = (Class) t;
            findTypeArguments(c.getGenericSuperclass());
        }
    }

    public Type getMyType()
    {
        // How do I return the type of T? (your question)
        return realType;
    }
}

클래스 계층의 수준 수에 관계없이 이 솔루션은 다음과 같이 계속 작동합니다.

public class FirstLevelChild<T> extends GenericClass<T> {

}

public class SecondLevelChild extends FirstLevelChild<String> {

}

이 경우 getMyType() = java.disc를 입력합니다.스트링

요령은 다음과 같습니다.

public class Main {

    public static void main(String[] args) throws Exception {

        System.out.println(Main.<String> getClazz());

    }

    static <T> Class getClazz(T... param) {

        return param.getClass().getComponentType();
    }

}

제 해결책은 이렇습니다.예를 들어 설명해야 합니다.유일한 요건은 서브클래스가 오브젝트가 아닌 범용 유형을 설정해야 한다는 것입니다.

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;

public class TypeUtils {

    /*** EXAMPLES ***/

    public static class Class1<A, B, C> {

        public A someA;
        public B someB;
        public C someC;

        public Class<?> getAType() {
            return getTypeParameterType(this.getClass(), Class1.class, 0);
        }

        public Class<?> getCType() {
            return getTypeParameterType(this.getClass(), Class1.class, 2);
        }
    }

    public static class Class2<D, A, B, E, C> extends Class1<A, B, C> {

        public B someB;
        public D someD;
        public E someE;
    }

    public static class Class3<E, C> extends Class2<String, Integer, Double, E, C> {

        public E someE;
    }

    public static class Class4 extends Class3<Boolean, Long> {

    }

    public static void test() throws NoSuchFieldException {

        Class4 class4 = new Class4();
        Class<?> typeA = class4.getAType(); // typeA = Integer
        Class<?> typeC = class4.getCType(); // typeC = Long

        Field fieldSomeA = class4.getClass().getField("someA");
        Class<?> typeSomeA = TypeUtils.getFieldType(class4.getClass(), fieldSomeA); // typeSomeA = Integer

        Field fieldSomeE = class4.getClass().getField("someE");
        Class<?> typeSomeE = TypeUtils.getFieldType(class4.getClass(), fieldSomeE); // typeSomeE = Boolean


    }

    /*** UTILS ***/

    public static Class<?> getTypeVariableType(Class<?> subClass, TypeVariable<?> typeVariable) {
        Map<TypeVariable<?>, Type> subMap = new HashMap<>();
        Class<?> superClass;
        while ((superClass = subClass.getSuperclass()) != null) {

            Map<TypeVariable<?>, Type> superMap = new HashMap<>();
            Type superGeneric = subClass.getGenericSuperclass();
            if (superGeneric instanceof ParameterizedType) {

                TypeVariable<?>[] typeParams = superClass.getTypeParameters();
                Type[] actualTypeArgs = ((ParameterizedType) superGeneric).getActualTypeArguments();

                for (int i = 0; i < typeParams.length; i++) {
                    Type actualType = actualTypeArgs[i];
                    if (actualType instanceof TypeVariable) {
                        actualType = subMap.get(actualType);
                    }
                    if (typeVariable == typeParams[i]) return (Class<?>) actualType;
                    superMap.put(typeParams[i], actualType);
                }
            }
            subClass = superClass;
            subMap = superMap;
        }
        return null;
    }

    public static Class<?> getTypeParameterType(Class<?> subClass, Class<?> superClass, int typeParameterIndex) {
        return TypeUtils.getTypeVariableType(subClass, superClass.getTypeParameters()[typeParameterIndex]);
    }

    public static Class<?> getFieldType(Class<?> clazz, AccessibleObject element) {
        Class<?> type = null;
        Type genericType = null;

        if (element instanceof Field) {
            type = ((Field) element).getType();
            genericType = ((Field) element).getGenericType();
        } else if (element instanceof Method) {
            type = ((Method) element).getReturnType();
            genericType = ((Method) element).getGenericReturnType();
        }

        if (genericType instanceof TypeVariable) {
            Class<?> typeVariableType = TypeUtils.getTypeVariableType(clazz, (TypeVariable) genericType);
            if (typeVariableType != null) {
                type = typeVariableType;
            }
        }

        return type;
    }

}

범용 유형을 사용하여 변수를 저장하는 경우 getClassType 메서드를 추가하는 이 문제를 다음과 같이 쉽게 해결할 수 있습니다.

public class Constant<T> {
  private T value;

  @SuppressWarnings("unchecked")
  public Class<T> getClassType () {
    return ((Class<T>) value.getClass());
  }
}

제공된 클래스 개체를 나중에 사용하여 다음과 같이 지정된 클래스의 인스턴스인지 확인합니다.

Constant<?> constant = ...;
if (constant.getClassType().equals(Integer.class)) {
    Constant<Integer> integerConstant = (Constant<Integer>)constant;
    Integer value = integerConstant.getValue();
    // ...
}
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
    return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}

다음과 같은 수업이 있는 경우:

public class GenericClass<T> {
    private T data;
}

T를 할 수 있습니다.T 삭제:

System.out.println(data.getClass().getSimpleName()); // "String", "Integer", etc.

스프링을 사용하는 경우:

public static Class<?>[] resolveTypeArguments(Class<?> parentClass, Class<?> subClass) {
    if (subClass.isSynthetic()) {
        return null;
    }
    return GenericTypeResolver.resolveTypeArguments(subClass, parentClass);
}

나나나 by.GenericTypeResolver이러한 클래스의 범용 정보는 컴파일 후에 완전히 삭제되었기 때문에, 전술한 질문과 같이 서브클래스가 아닌 클래스에 대해서는 null이 됩니다.

이 문제를 해결하는 유일한 방법은 다음과 같습니다.

public class GenericClass<T>
{
    private final Class<T> clazz;
    public Foo(Class<T> clazz) {
        this.clazz= clazz;
    }
    
    public Type getMyType()
    {
        return clazz;
    }
}

범용 클래스를 변경할 수 없고 이 페이지에서 이미 설명한 메서드 중 하나를 사용할 수 없는 경우 런타임인스턴스 클래스 이름을 기반으로 유형 클래스를 가져오는 간단한 방법이 있습니다.

Class getType(GenericType runtimeClassMember){
if (ClassA.class.equals(runtimeClassMember.getClass()){
  return TypeForClassA.class;
} else if (ClassB.class.equals(runtimeClassMember.getClass()){
  return TypeForClassB.class;
} 

//throw an expectation or do whatever you want for the cases not described in the if section.
}

@Moesio Above와 같은 작업을 수행했지만 Kotlin에서는 다음과 같이 수행할 수 있습니다.

class A<T : SomeClass>() {

    var someClassType : T

    init(){
    this.someClassType = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<T>
    }

}

이건 파블로와 쿨마인드의 대답에서 영감을 얻은 거야때때로 나는 kayz1의 답변(다른 많은 답변에서도 표현)의 기술을 사용하기도 했고, OP가 요구한 것을 실행하는 것은 적절하고 신뢰할 수 있는 방법이라고 생각한다.

우선 이 기능을 이용할 수 있는 기존 타입, 특히 이종 범용 유니언 타입이 있기 때문에 이것을 인터페이스(PJWeisberg와 유사)로 정의하기로 했습니다.

public interface IGenericType<T>
{
    Class<T> getGenericTypeParameterType();
}

범용 어나니머스인터페이스 실장에서의 간단한 실장은 다음과 같습니다.

//Passed into the generic value generator function: toStore
//This value name is a field in the enclosing class.
//IUnionTypeValue<T> is a generic interface that extends IGenericType<T>
value = new IUnionTypeValue<T>() {
    ...
    private T storedValue = toStore;
    ...
    
    @SuppressWarnings("unchecked")
    @Override
    public Class<T> getGenericTypeParameterType()
    {
        return (Class<T>) storedValue.getClass();
    }
}

클래스 정의 오브젝트를 소스로 하여 구축함으로써 구현될 수도 있다고 생각합니다.이것은 별개의 사용 예에 지나지 않습니다.중요한 것은 다른 답변과 마찬가지로 어떤 식으로든 실행 시 유형 정보를 가져와야 런타임에 사용할 수 있다는 것입니다.오브젝트 자체는 유형을 유지하지만 (다른 사람이 말한 것처럼 적절한 참조를 사용하여) 삭제하면 해당 유형 정보가 손실됩니다.

누군가에게 유용할 수도 있어요.java.lang.ref 를 사용할 수 있습니다.WeakReference:

class SomeClass<N>{
  WeakReference<N> variableToGetTypeFrom;

  N getType(){
    return variableToGetTypeFrom.get();
  }
}

나는 이것이 이해하기 쉽고 쉽게 설명할 수 있는 해결책이라는 것을 알았다.

public class GenericClass<T> {

    private Class classForT(T...t) {
        return t.getClass().getComponentType();
    }

    public static void main(String[] args) {
        GenericClass<String> g = new GenericClass<String>();

        System.out.println(g.classForT());
        System.out.println(String.class);
    }
}

언급URL : https://stackoverflow.com/questions/3403909/get-generic-type-of-class-at-runtime

반응형