如何解决是否有更好更安全的方法来获取参数化类型的类?
我想创建一个返回参数化类型的类的方法。
考虑一个interface
:
private static interface MyInterface<T> {
void run(T parameter);
}
一个实现:
private static class MyInterfaceImplString implements MyInterface<String> {
@Override
public void run(String parameter) { }
}
现在,我想将MyInterfaceImplString.class
传递给一个方法,该方法将返回String.class
。
我正在打印东西,我可以看到那里的信息,但是我无法获取它。或至少以一种更安全的方式。
public class TypesTest {
public static void main(String[] args) {
Class<?> genericParameter1 = getGenericParamaterType(MyInterfaceImplVoid.class);
System.out.println(genericParameter1); //expect Void here
Class<?> genericParameter2 = getGenericParamaterType(MyInterfaceImplString.class);
System.out.println(genericParameter2); //expect String here
}
private static Class<?> getGenericParamaterType(Class<? extends MyInterface<?>> clazz) {
for (Method m : clazz.getMethods()) {
if ("run".equals(m.getName()) && m.getParameterCount() == 1) {
System.out.println(TypeLiteral.get(clazz).getParameterTypes(m));
}
}
return null;
}
private static class MyInterfaceImplVoid implements MyInterface<Void> {
@Override
public void run(Void parameter) { }
}
private static class MyInterfaceImplString implements MyInterface<String> {
@Override
public void run(String parameter) { }
}
private static interface MyInterface<T> {
void run(T parameter);
}
}
忽略方法中的null
返回值。我只想打印东西,看看得到了什么,然后将其退回。但是,我当前的实现似乎有点不合常规,因为该类可能有多个与run
无关的MyInterface
方法。
在使用a XY problem的情况下,我希望能够识别出其中哪些实现包含extends Employee
的参数化类型(比方说)。我间接给定了run
或HourlyPaidEmployee
来调用接口的MonthlyPaidEmployee
方法。因此,如果基础实现是MyInterface<Employee>
,则可以注入我的实际员工(按月或按小时支付)。但是,如果实现是MyInterface<HourlyEmployee>
,则无法注入按月付费。因此,获取参数化类型的类可以帮助我了解可以安全地注入run
方法的雇员类型。
我在类路径中使用java-8和Guice
依赖性,其中包含guava
依赖性。
编辑:@Glorfindel的回答很明确。但是,如果实现看起来像这样:
private static class MyInterfaceImplVoid implements MyInterface<Void> {
@Override
public void run(Void parameter) {
}
public void run(Integer par) {
}
}
我不知道引用该接口的方法是什么,因此他的回答可能会返回给我Integer.cass
。
解决方法
当您要查找 type 的实际类型参数时,不应使用方法。您可以像使用这样简单的方案来实现它
public class TypesTest {
public static void main(String[] args) {
Type genericParameter1 = getGenericParameterType(MyInterfaceImplVoid.class);
System.out.println(genericParameter1+","+(genericParameter1==Void.class));
Type genericParameter2 = getGenericParameterType(MyInterfaceImplString.class);
System.out.println(genericParameter2+","+(genericParameter2==String.class));
}
private static Type getGenericParameterType(Class<? extends MyInterface<?>> clazz) {
for(Type interfaceType: clazz.getGenericInterfaces()) {
if(interfaceType == clazz)
throw new IllegalArgumentException("raw implementation");
if(interfaceType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType)interfaceType;
if(pt.getRawType() == MyInterface.class)
return pt.getActualTypeArguments()[0];
}
}
throw new UnsupportedOperationException("not implemented directly");
}
}
class java.lang.Void,true
class java.lang.String,true
但是请注意,返回类型是Type
,而不是类,因为不能保证类型参数是Class
(可更改类型或原始类型)。它本身可以是参数化类型,也可以是类型变量。
考虑类似情况
abstract class InTheMiddle<T> implements MyInterface<T> {}
class Implementation extends InTheMiddle<String> {
@Override
public void run(String parameter) {
}
}
遍历类型层次结构,查询declared type variables并将其与实际类型参数匹配(当它们出现在要解析的目标类型变量上时),这将是可解决的。
但是请注意,以下也是可能的:
abstract class Outer<T> {
class Inner implements MyInterface<T> {
@Override
public void run(T parameter) {
}
}
}
class SubclassOfOuter extends Outer<Integer> {
}
SubclassOfOuter outer = new SubclassOfOuter();
MyInterface<Integer> object = outer.new Inner();
这将需要遍历类型层次结构和词法作用域的工件(除了外部类之外,实现类可以是放置在泛型类内部的泛型方法的本地类,而泛型类可能是其他方法的本地类……)。
然后,由于类型擦除,有些结构在运行时根本无法查询:
class GenericImpl<X> implements MyInterface<X> {
@Override
public void run(X parameter) {
}
}
GenericImpl<String> a = new GenericImpl<>();
GenericImpl<Integer> b = new GenericImpl<>();
或
MyInterface<Thread> another = t -> {};
因此,在使代码依赖于在运行时确定实际类型参数的能力之前,请三思。
,如果我替换
System.out.println(TypeLiteral.get(clazz).getParameterTypes(m));
使用
return m.getParameterTypes()[0];
我得到以下输出:
class java.lang.Void
class java.lang.String
不需要Guice / Guava,普通的旧Java似乎可以正常工作。对于其他问题,我不知道有什么好的解决方案。似乎反射甚至无法检测到一种方法是替代方法,而另一种方法不是。如果您对实现类有足够的控制权,我建议您仅使用其他名称代替其他方法的run
。
您的T
未绑定。
这意味着该界面的每个隐含内容,即使您的MyInterfaceImplVoid
, 也将具有run(Object)
方法,即使您根本没有编写任何方法,它也会在这种情况下将参数强制转换为Void
并将调用继续传递到run(Void)
。因此,如果要确定调用接口方法,请始终调用run(Object)
,并发送正确类型的对象(通过@Glorfindel的答案得出)。
请注意,弄清楚界限的这种“技巧”通常是一个不好的计划。当我写这节课时你打算做什么?
class DynamicMyInterface<Z> implements MyInterface<Z> {
private Class<Z> forcedType;
public DynamicMyInterface(Class<Z> type) {
this.forcedType = type;
}
public void run(Z in) {
forcedType.cast(in);
....
}
}
您对.getGenericType()
的干扰使从不无法确定原因是由于擦除。除了Z extends Object
,您将一无所获。
我找到了一种使用这种“丑陋”-“到底是什么”方法来获取参数化类型的类的方法:
private static Class<?> getGenericParamaterType(Class<? extends MyInterface<?>> clazz) {
for (TypeToken<?> typeToken : TypeToken.of(clazz).getTypes()) {
Class<?> rawType = typeToken.getRawType();
if (rawType == MyInterface.class) {
TypeLiteral<?> typeLiteral = TypeLiteral.get(typeToken.getType());
for (Method method : rawType.getMethods()) {
if ("run".equals(method.getName())) {
TypeLiteral<?> typeLiteral2 = typeLiteral.getParameterTypes(method).get(0);
return typeLiteral2.getRawType();
}
}
}
}
return null;
}
它完全按照我的要求工作。但是,我不确定它的性能。另外,我真的认为Guava提供了一种更简洁的方法来实现我想要的。另一方面,我找不到它。
下面是显示其工作原理的完整示例。即使在实现中实现了多个run
方法的复杂情况下,甚至在没有实现该方法的抽象类中也是如此。
public class TypesTest {
public static void main(String[] args) {
Class<?> genericParameter1 = getGenericParamaterType(MyInterfaceImplVoid.class);
System.out.println(genericParameter1); //expect Void here
Class<?> genericParameter2 = getGenericParamaterType(MyInterfaceImplString.class);
System.out.println(genericParameter2); //expect String here
Class<?> genericParameter3 = getGenericParamaterType(AbstractImpl.class);
System.out.println(genericParameter3); //expect Integer here
}
private static Class<?> getGenericParamaterType(Class<? extends MyInterface<?>> clazz) {
for (TypeToken<?> typeToken : TypeToken.of(clazz).getTypes()) {
Class<?> rawType = typeToken.getRawType();
if (rawType == MyInterface.class) {
TypeLiteral<?> typeLiteral = TypeLiteral.get(typeToken.getType());
for (Method method : rawType.getMethods()) {
if ("run".equals(method.getName())) {
TypeLiteral<?> typeLiteral2 = typeLiteral.getParameterTypes(method).get(0);
return typeLiteral2.getRawType();
}
}
}
}
return null;
}
private static class MyInterfaceImplVoid extends JPanel implements MyInterface<Void>,Runnable {
@Override
public void run(Void parameter) {
}
public void run(Integer par) { }
public void run(String s) { }
@Override
public void someOtherMethod() { }
@Override
public void run() { }
}
private static abstract class AbstractImpl implements MyInterface<Integer> {
}
private static class MyInterfaceImplString implements MyInterface<String> {
@Override
public void run(String parameter) { }
@Override
public void someOtherMethod() { }
}
private static interface MyInterface<T> {
void run(T parameter);
void someOtherMethod();
}
}
我不会将此答案标记为已接受,因为我非常确定这样做会更方便。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。