[英]Retrieve the classes used within an instance of a Generic Type using Reflection
[英]Using Generics to retrieve a list of classes of type Generic
我对 Java 中的 generics 比较陌生,所以如果这是在学校教授的常见内容,我很抱歉(我几乎是自学的)。 假设我有下面的接口和抽象 class
public interface IChallenge<T> {
boolean handle(T e);
Class<? extends T> getType();
}
public abstract class AbstractChallenge<T> implements IChallenge<T> {
protected Class<T> clazz;
@Override
public Class<? extends T> getType() {
return this.clazz;
}
}
对于每个扩展 AbstractChallenge 的 class, handle
方法接受为泛型指定的参数。 因此,如果我有一个在Event
发生时触发的事件 class,我会
public class EventChallenge extends AbstractChallenge<Event> {
public EventChallenge() {
super(Event.class);
}
@Override
public boolean handle(Event e) {}
}
当我尝试将特定的 class 传递给handle
方法时,我的问题就出现了。 由于泛型可以是任何 class,并且同一类型可能存在多个挑战,因此我将挑战存储在 map 中,其类型为键。
private Map<Something, List<AbstractChallenge<Something>> challenges = new HashMap<>();
最终希望实现以下目标
List<AbstractChallenge<A>> specificChallenges = this.challenges.get(A.class);
specificChallenges.removeIf(challenge -> challenge.handle(A));
但是我很难弄清楚“某事”中的内容。 如果我放通配符?
符号,然后 IntelliJ 说handle
必须接受要求的参数:捕获? 当我通过它时 class A. 我得到的最好的方法是不指定AbstractChallenge
的类型,但我想要一个更好的解决方案。
有任何想法吗? 谢谢!
您所寻求的是类似的东西(我在这里发表了评论):
private Map<Class<?>, List<IChallenge<?>> challenges = new HashMap<>();
A a = ...;
challenges.get(a.getClass())
.removeIf(challenger -> challenger.handle(a));
这是不安全的,因为你不知道 T 的实际类型(编译器不知道,所以它可以做的就是推断它,在这种情况下,类型将是Object
):
Integer.class
IChallenge<T>
并且如果T
不是Integer
(或Number
, Object
,例如: T
层次结构中的任何类型),它可能会失败,如果实现使用 ZA8CFDE63311BD59EB266AC96B 它处理8添加时:
challenges.get(Integer.class).add((Number a) -> a.intValue() > 10); // #1
challenges.get(Integer.class).add((Integer a) -> a.intValue() > 10); // #2
challenges.get(Integer.class).add((Object a) -> a != null); // #3
challenges.get(Integer.class).add((String a) -> a.length() > 10); // #4
这是一个例子:
Integer a = Integer.valueOf(5);
// #1 -> ok: a is a Number
challenges.get(a.getClass()).removeIf(c -> c.handle(a));
// #2 -> ok: a is an Integer
challenges.get(a.getClass()).removeIf(c -> c.handle(a));
// #3 -> ok: a is an Object
challenges.get(a.getClass()).removeIf(c -> c.handle(a));
// #4 ->ko: a is not a String
challenges.get(a.getClass()).removeIf(c -> c.handle(a));
如果您希望避免这种情况,但仍然能够应对任何挑战,则应确保持有/构建挑战的 class 正确完成:
public <T> void addChallenge(Class<T> type, IChallenge<T> challenge) {
challenges.computeIfAbsent(type, ignored -> new ArrayList<>()).add(challenge);
}
虽然您可以使用您在IChallenge
中定义的getType()
,但我想向您展示如何强制保护类型(键)和IChallenge
(值):通常,除非您授予对 map 的写入权限其他类,这应该是安全的,因为编译器会在插入时验证类型。
因此,当您删除它们时,由于IChallenge
的类型参数,您永远不应该有ClassCastException
。
你也可以试试玩? super T
? super T
和? extends T
? extends T
但这是另一个挑战。
--
关于您的评论:
我不完全确定如何使用您指定的 addChallenge 方法。 现在,对于创建的每个挑战,我都有一个 Class> 列表,当应该加载特定挑战时,程序使用.newInstance() 进行实例化。 我应该采取不同的做法吗? 我只需要一次加载一定数量的挑战,而不是全部——DeprecatedFrank
我不是要立即加载所有挑战,我只是告诉您使用 OOP 来确保没有人,但是您的挑战持有者(我们称之为ChallengeHolder
)管理 map,并对其进行管理,以便您避免 Z56B97998B338B9F37EEZFA 陷阱:846B9F370FF5
class ChallengeHolder {
private final Map<Class<?>, List<IChallenge<?>>> challenges;
public ChallengeHolder() {
this.challenges = new HashMap<>();
}
public <T> void addChallenge(Class<T> type, IChallenge<T> challenge) {
challenges.computeIfAbsent(type, ignored -> new ArrayList<>()).add(challenge);
}
public boolean handle(Object a) {
List<IChallenge<T>> challengers = challenges.get(a);
if (challengers == null) return false;
return challengers.removeIf(c -> c.handle(a));
}
}
由于除了ChallengeHolder
class 提供的挑战之外,没有公共访问权限,因此使用Object
或Class<T>
应该没有问题。
如果您需要按需创建IChallenge
,那么您也许可以像这样实现:
public class LazyChallenge<T> implements IChallenge<T> {
private final Class<IChallenge<T>> impl;
private IChallenge<T> value;
public LazyChallenge(IChallenge<T> impl) {
this.impl = impl;
}
public boolean handle(T o) {
if (value == null) {
try {
value = impl.getConstructor().newInstance();
} catch (java.lang.ReflectiveOperationException e) { // ... a bunch of exception your IDE will fill in ...
throw new IllegalStateException(e);
}
}
return value.handle(o);
}
}
然后,您将其添加到ChallengeHolder
:
challengeHolder.addChallenge(String.class, new LazyChallenge<>(StringChallenge.class));
或者您可以使用 lambda 来避免反射:
public class LazyChallenge<T> implements IChallenge<T> {
private final Class<IChallenge<T>> supplier;
private IChallenge<T> value;
public LazyChallenge(Supplier<IChallenge<T>> supplier) {
this.supplier = supplier;
}
public boolean handle(T o) {
if (value == null) {
value = supplier.get();
}
return value.handle(o);
}
}
和:
challengeHolder.addChallenge(String.class, new LazyChallenge<>(StringChallenge::new));
之后,您可以直接在ChallengeHolder
中使用Supplier
代替IChallenge
:
class ChallengeHolder {
private final Map<Class<?>, List<Supplier<IChallenge<?>>>> challenges;
public ChallengeHolder() {
this.challenges = new HashMap<>();
}
public <T> void addChallenge(Class<T> type, Supplier<IChallenge<T>> challenge) {
challenges.computeIfAbsent(type, ignored -> new ArrayList<>()).add(challenge);
}
public boolean handle(Object a) {
List<IChallenge<T>> challengers = challenges.get(a);
if (challengers == null) return false;
return challengers.removeIf(c -> c.get().handle(a));
}
}
StringChallenge existing = ... ;
// always reuse an existing
challengeHolder.addChallenge(String.class, () -> existing);
// bring a new challenge each time that ChallengeHolder::handle is called
challengeHolder.addChallenge(String.class, StringChallenge::new);
如果我要实现它,我会使用 lambda 方式,因为您可以避免反射陷阱(try catch,可见性问题,特别是考虑到 Java 9++ 引入了模块,...)。
上面定义的LazyChallenge
可能有助于避免创建多个StringChallenge
。 在这种情况下,最好让它实现Supplier<T>
而不是IChallenge<T>
。
这整个题外话并没有改变我之前指出的:确保只有ChallengeHolder
读/写 map。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.