Java에서 일반 매개 변수 유형 가져오기(반영 포함)
제네릭 파라미터의 타입을 얻을 수 있는가?
예:
public final class Voodoo {
public static void chill(List<?> aListWithTypeSpiderMan) {
// Here I'd like to get the Class-Object 'SpiderMan'
Class typeOfTheList = ???;
}
public static void main(String... args) {
chill(new ArrayList<SpiderMan>());
}
}
한 건축물은, 내가 우연히 마주친 적이 있다.
Class<T> persistentClass = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
그래서 불행히도 내가 완전히 이해하지 못하는 반성의 마법이 있는 것 같다...미안하다
@DerMike의 대답을 분해하여 다음과 같이 설명하고 싶다.
첫째, 유형 삭제는 JDK가 유형 정보를 런타임에 제거한다는 것을 의미하지 않는다.컴파일 시간 유형 확인과 런타임 유형 호환성이 동일한 언어로 공존할 수 있도록 하는 방식이다.이 코드 블록이 암시하듯이, JDK는 삭제된 유형 정보를 유지하며, 이는 단지 체크된 캐스트나 물건과는 관련이 없다.
둘째, 이것은 점검 중인 콘크리트 유형으로부터 정확히 한 단계 높은 수준의 상속권(즉, 일반적 유형 매개변수를 가진 추상적 부모 계층은 그것으로부터 직접 계승되는 그 자체의 콘크리트 유형 매개변수로부터 직접 상속되는 자체의 구체적인 구현을 위해 해당 유형 매개변수에 해당하는 콘크리트 유형을 찾을 수 있다.이 클래스가 추상화되지 않고 인스턴스화되거나 구체적인 구현이 두 단계 아래인 경우(조금 지미밍을 수행하면 1단계 이상의 미리 결정된 수준 수 또는 X 일반 유형 매개변수(et cetera)를 가진 최저 등급까지 적용할 수 있지만) 이 클래스는 작동하지 않을 것이다.
어쨌든, 설명에 따라.참조하기 쉽도록 행으로 구분된 코드:
1# Class genericParameter0OfThisClass =2# (클래스)3# (ParameterizedType)4# getClass()5# .getGenericSuperclass()6# .getActualTypeArguments()[0];
'우리'는 이 코드를 포함하는 일반 유형의 추상 클래스가 되도록 하자.대략적으로 읽어보기:
- 4호선은 현재 콘크리트 클래스 예제를 받는다.이것은 우리의 직계 후손의 구체적인 유형을 확인시켜 준다.
- 5호선은 그 수업의 슈퍼타입이 유형으로 되어있어; 이것이 우리야.우리는 파라메트릭 타입이기 때문에 파라메트릭화된 것에 안전하게 몸을 던질 수 있다.유형(3행)핵심은 Java가 이 유형 개체를 결정할 때 하위 항목에 있는 유형 정보를 사용하여 유형 정보를 새로운 매개 변수화된 우리의 유형 매개 변수와 연결한다는 것이다.인스턴스를 입력하십시오.그래서 이제 우리는 우리의 제네릭을 위한 구체적인 종류에 접근할 수 있다.
- 6번 라인은 클래스 코드에 명시된 순서대로 우리의 제네릭에 매핑된 타입의 배열을 얻는다.이 예에서는 첫 번째 파라미터를 추출한다.이것은 유형으로 돌아온다.
- 라인 2는 클래스에 반환되는 최종 유형을 주조한다.이것은 안전하다. 왜냐하면 우리는 우리의 제네릭 타입 매개변수가 어떤 유형을 취할 수 있는지 알고 있고 그것들이 모두 클래스가 될 것인지 확인할 수 있기 때문이다(Java에서는 실제로 클래스 인스턴스가 없는 제네릭 매개변수를 얻는 방법을 잘 모르겠다).
...그리고 거의 다야.그래서 우리는 우리 자신의 구체적인 구현으로부터 얻은 유형 정보를 우리 자신에게 다시 밀어넣고, 그것을 클래스 핸들에 접근하는데 사용한다.getGenericSuperclass()를 두 배로 올리고 두 단계로 올라가거나 getGenericSuperclass()를 제거하고 구체적인 유형으로서 우리 자신을 위한 가치를 얻을 수 있다(caveat:나는 이 시나리오들을 시험해 본 적이 없다. 그들은 아직 나를 위해 올라오지 않았다.)
콘크리트 자녀가 임의의 수의 깡충깡충 뛰어가거나, 콘크리트이거나 최종적이지 않은 경우, 특히 (가변적으로 깊은) 자녀 중 어느 한 명이라도 자신의 생식기를 가질 것으로 예상하는 경우 까다로워진다.하지만 보통은 그런 고려사항들을 중심으로 설계할 수 있기 때문에, 이것이 당신에게 대부분의 길을 열어준다.
이게 누군가에게 도움이 되었기를 바래!나는 이 포스트가 오래된 것임을 알고 있다.나는 아마도 이 설명을 슬쩍하고 다른 질문들을 위해 그것을 보관할 것이다.
사실 난 이걸 작동시켰어.다음 조각을 고려하십시오.
Method m;
Type[] genericParameterTypes = m.getGenericParameterTypes();
for (int i = 0; i < genericParameterTypes.length; i++) {
if( genericParameterTypes[i] instanceof ParameterizedType ) {
Type[] parameters = ((ParameterizedType)genericParameterTypes[i]).getActualTypeArguments();
//parameters[0] contains java.lang.String for method like "method(List<String> value)"
}
}
나는 jdk 1.6을 사용하고 있다.
실제로 "익명 클래스" 트릭과 슈퍼타입 토큰의 아이디어를 적용함으로써 해결책이 있다.
public final class Voodoo {
public static void chill(final List<?> aListWithSomeType) {
// Here I'd like to get the Class-Object 'SpiderMan'
System.out.println(aListWithSomeType.getClass().getGenericSuperclass());
System.out.println(((ParameterizedType) aListWithSomeType
.getClass()
.getGenericSuperclass()).getActualTypeArguments()[0]);
}
public static void main(String... args) {
chill(new ArrayList<SpiderMan>() {});
}
}
class SpiderMan {
}
비결은 익명 수업을 만드는 데 있다.new ArrayList<SpiderMan>() {}
에, 원본의 자에에에(simple)new ArrayList<SpiderMan>()
한 경우한다. Anoymous 클래스의 사용(가능한 경우)은 컴파일러가 형식 인수에 대한 정보를 보유하도록 보장한다.SpiderMan
형식 매개 변수에 주어짐List<?>
짜잔!
중복 방지를 위해 Java-8 기본 방법 내에서 #getGenericInterfaces() 메서드를 사용하는 매개 변수화된 인터페이스의 일반 매개 변수를 얻기 위한 @DerMike의 답변 부록:
import java.lang.reflect.ParameterizedType;
public class ParametrizedStuff {
@SuppressWarnings("unchecked")
interface Awesomable<T> {
default Class<T> parameterizedType() {
return (Class<T>) ((ParameterizedType)
this.getClass().getGenericInterfaces()[0])
.getActualTypeArguments()[0];
}
}
static class Beer {};
static class EstrellaGalicia implements Awesomable<Beer> {};
public static void main(String[] args) {
System.out.println("Type is: " + new EstrellaGalicia().parameterizedType());
// --> Type is: ParameterizedStuff$Beer
}
아니, 그건 불가능해.다운다운 호환성 문제로 인해 Java의 제네릭은 유형 삭제(즉, 런타임에 비 제네릭만 있는 경우)를 기반으로 한다.List
이의를 제기하다런타임에 형식 매개 변수에 대한 일부 정보가 있지만, 객체 인스턴스가 아닌 클래스 정의(즉, "이 필드의 정의에서 사용하는 일반 유형이 무엇인가?")에 상주한다.
유형 삭제로 인해 목록 유형을 알 수 있는 유일한 방법은 형식에 매개 변수로 전달되는 것이다.
public class Main {
public static void main(String[] args) {
doStuff(new LinkedList<String>(), String.class);
}
public static <E> void doStuff(List<E> list, Class<E> clazz) {
}
}
@bertolami가 지적한 바와 같이, 우리에게 변수 타입과 그것의 미래 가치(typeOfList 변수의 내용)를 얻을 수 없다.
그럼에도 불구하고 다음과 같이 클래스를 매개 변수로 전달할 수 있다.
public final class voodoo {
public static void chill(List<T> aListWithTypeSpiderMan, Class<T> clazz) {
// Here I'd like to get the Class-Object 'SpiderMan'
Class typeOfTheList = clazz;
}
public static void main(String... args) {
chill(new List<SpiderMan>(), Spiderman.class );
}
}
그것은 당신이 클래스 변수를 활동의 생성자에게 전달해야 할 때 구글이 하는 일이다.계측TestCase2.
아니 그건 불가능해.
클래스가 해당 규칙의 유일한 예외인 일반적 유형의 필드를 얻을 수 있으며, 그마저도 약간의 해킹을 할 수 있다.
그 예제는 Java에서 제네릭 유형 파악을 참조하십시오.
나는 많은 사람들이 그쪽으로 기우는 것을 보았다.getGenericSuperclass()
솔루션:
class RootGeneric<T> {
public Class<T> persistentClass = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
그러나 이 솔루션은 오류가 발생하기 쉽다.후손에 제네릭이 있으면 제대로 작동하지 않는다.다음을 고려하십시오.
class Foo<S> extends RootGeneric<Integer> {}
class Bar extends Foo<Double> {}
어떤 타입이 될 것인가.Bar.persistentClass
가지고 있니?Class<Integer>
아니, 그럴거야Class<Double>
이 일어날 이다.때문에 이런일이 일어날것이다.getClass()
항상 최상위 클래스를 반환한다.Bar
이 경우에, 그리고 그것의 일반적인 슈퍼클래스는Foo<Double>
인수 입니다.따라서, 인수 유형은 다음과 같다.Double
.
만약 네가 실패하지 않는 믿을만한 해결책이 필요하다면 나는 두 가지를 제안할 수 있다.
- 사용하다
Guava
. 정확히 이러한 목적을 위해 만들어진 클래스가 있다.com.google.common.reflect.TypeToken
모든 코너 케이스를 잘 처리하고 좀 더 멋진 기능을 제공한다.단점은 추가 의존이다.이 클래스를 사용한 적이 있는 경우, 다음과 같이 간단하고 명확해 보일 수 있다.
class RootGeneric<T> {
@SuppressWarnings("unchecked")
public final Class<T> persistentClass = (Class<T>) (new TypeToken<T>(getClass()) {}.getType());
}
- 아래 사용자 지정 방법을 사용하십시오.위에서 언급한 과바 등급과 유사하게 상당히 단순화된 논리를 구현한다.하지만, 나는 그것이 실수하기 쉽다고 장담하지 않는다.하지만 그것은 일반 후손들과 함께 문제를 해결한다.
abstract class RootGeneric<T> {
@SuppressWarnings("unchecked")
private Class<T> getTypeOfT() {
Class<T> type = null;
Class<?> iter = getClass();
while (iter.getSuperclass() != null) {
Class<?> next = iter.getSuperclass();
if (next != null && next.isAssignableFrom(RootGeneric.class)) {
type =
(Class<T>)
((ParameterizedType) iter.getGenericSuperclass()).getActualTypeArguments()[0];
break;
}
iter = next;
}
if (type == null) {
throw new ClassCastException("Cannot determine type of T");
}
return type;
}
}
내가 여기서 찾은 이 예에서와 같이 반사가 있는 일반 매개변수의 유형을 얻을 수 있다.
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
public class Home<E> {
@SuppressWarnings ("unchecked")
public Class<E> getTypeParameterClass(){
Type type = getClass().getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) type;
return (Class<E>) paramType.getActualTypeArguments()[0];
}
private static class StringHome extends Home<String>{}
private static class StringBuilderHome extends Home<StringBuilder>{}
private static class StringBufferHome extends Home<StringBuffer>{}
/**
* This prints "String", "StringBuilder" and "StringBuffer"
*/
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Object object0 = new StringHome().getTypeParameterClass().newInstance();
Object object1 = new StringBuilderHome().getTypeParameterClass().newInstance();
Object object2 = new StringBufferHome().getTypeParameterClass().newInstance();
System.out.println(object0.getClass().getSimpleName());
System.out.println(object1.getClass().getSimpleName());
System.out.println(object2.getClass().getSimpleName());
}
}
질문에 대한 빠른 대답은 Java 제네릭 유형 삭제 때문에 할 수 없다는 것이다.
더 긴 대답은 다음과 같은 목록을 작성한 경우일 것이다.
new ArrayList<SpideMan>(){}
그리고 이 경우 일반형은 위의 새로운 익명계급의 일반 슈퍼클래스에 보존된다.
내가 리스트를 가지고 이것을 하는 것을 추천하는 것은 아니지만, 그것은 청취자 구현이다.
new Listener<Type>() { public void doSomething(Type t){...}}
그리고 슈퍼 클래스 및 슈퍼 인터페이스의 일반적인 유형을 추론하는 것은 JVM들 사이에서 변화하기 때문에, 일반 솔루션은 일부 답변이 시사하는 것처럼 그렇게 직선적이지 않다.
여기 또 다른 속임수가 있다.일반 바라그 배열 사용
import java.util.ArrayList;
class TypedArrayList<E> extends ArrayList<E>
{
@SafeVarargs
public TypedArrayList (E... typeInfo)
{
// Get generic type at runtime ...
System.out.println (typeInfo.getClass().getComponentType().getTypeName());
}
}
public class GenericTest
{
public static void main (String[] args)
{
// No need to supply the dummy argument
ArrayList<Integer> ar1 = new TypedArrayList<> ();
ArrayList<String> ar2 = new TypedArrayList<> ();
ArrayList<?> ar3 = new TypedArrayList<> ();
}
}
자바에서의 제네릭은 컴파일 시간에만 고려되기 때문에 이것은 불가능하다.따라서 자바 제네릭은 일종의 전처리기일 뿐이다.하지만 당신은 리스트의 멤버들의 실제 클래스를 얻을 수 있다.
받아들이거나 돌려줄 것으로 예상되는 방법들에 대해 코딩해놨어Iterable<?...>
암호는 다음과 같다.
/**
* Assuming the given method returns or takes an Iterable<T>, this determines the type T.
* T may or may not extend WindupVertexFrame.
*/
private static Class typeOfIterable(Method method, boolean setter)
{
Type type;
if (setter) {
Type[] types = method.getGenericParameterTypes();
// The first parameter to the method expected to be Iterable<...> .
if (types.length == 0)
throw new IllegalArgumentException("Given method has 0 params: " + method);
type = types[0];
}
else {
type = method.getGenericReturnType();
}
// Now get the parametrized type of the generic.
if (!(type instanceof ParameterizedType))
throw new IllegalArgumentException("Given method's 1st param type is not parametrized generic: " + method);
ParameterizedType pType = (ParameterizedType) type;
final Type[] actualArgs = pType.getActualTypeArguments();
if (actualArgs.length == 0)
throw new IllegalArgumentException("Given method's 1st param type is not parametrized generic: " + method);
Type t = actualArgs[0];
if (t instanceof Class)
return (Class<?>) t;
if (t instanceof TypeVariable){
TypeVariable tv = (TypeVariable) actualArgs[0];
AnnotatedType[] annotatedBounds = tv.getAnnotatedBounds();///
GenericDeclaration genericDeclaration = tv.getGenericDeclaration();///
return (Class) tv.getAnnotatedBounds()[0].getType();
}
throw new IllegalArgumentException("Unknown kind of type: " + t.getTypeName());
}
변수에서 일반 매개 변수를 가져올 수 없음.그러나 방법 또는 현장 선언에서 다음을 수행할 수 있다.
Method method = getClass().getDeclaredMethod("chill", List.class);
Type[] params = method.getGenericParameterTypes();
ParameterizedType firstParam = (ParameterizedType) params[0];
Type[] paramsOfFirstGeneric = firstParam.getActualTypeArguments();
내가 이 코드 조각을 읽는 것이 어려웠기 때문에, 나는 단지 읽을 수 있는 두 개의 행으로 나누었을 뿐이다.
// assuming that the Generic Type parameter is of type "T"
ParameterizedType p = (ParameterizedType) getClass().getGenericSuperclass();
Class<T> c =(Class<T>)p.getActualTypeArguments()[0];
내 메서드에 매개 변수가 없는 상태에서 Type 매개 변수의 인스턴스를 생성하려고 함:
publc T getNewTypeInstance(){
ParameterizedType p = (ParameterizedType) getClass().getGenericSuperclass();
Class<T> c =(Class<T>)p.getActualTypeArguments()[0];
// for me i wanted to get the type to create an instance
// from the no-args default constructor
T t = null;
try{
t = c.newInstance();
}catch(Exception e){
// no default constructor available
}
return t;
}
사용:
Class<?> typeOfTheList = aListWithTypeSpiderMan.toArray().getClass().getComponentType();
참조URL: https://stackoverflow.com/questions/1901164/get-type-of-a-generic-parameter-in-java-with-reflection
'IT이야기' 카테고리의 다른 글
c 어레이 - 경고: 문자열 리터럴이 아닌 형식 (0) | 2022.05.02 |
---|---|
Axios - 한 번의 호출만으로 헤더 권한 부여 제거 (0) | 2022.05.02 |
axi 및 Symfony를 사용하여 개체를 삭제할 때 상태 코드 500이 삭제되지만 삭제는 작동함 (0) | 2022.05.02 |
전언으로 주장하다. (0) | 2022.05.02 |
Eclipse에서 java.library.path를 설정하는 방법 (0) | 2022.05.02 |