花了相当长的时间后,我想我可以使用一些帮助。 基本上我试图创建一组处理器,每个处理器都接受自己要处理的对象类型。调用类需要能够根据对象的类型获取处理器并调用该处理器,并传递该对象。 这就是我创造的:
abstract class Processor<F> {
abstract void process(F foo);
}
class AProcessor extends Processor<Integer> {
public void process(Integer foo){
}
}
class BProcessor extends Processor<Long> {
public void process(Long foo){
}
}
最初我使用处理器接口,它对我来说并不重要。
然后调用类就是这样:
public class Test {
private final Map<Class<?>, Processor<?>> values = new HashMap<Class<?>, Processor<?>>();
public void main(String[] agrs){
Test t = new Test();
t.values.put(Integer.class, new AProcessor());
t.values.put(Long.class, new BProcessor());
AProcessor ap = t.get(Integer.class, Processor<Integer>.class);
}
public <T> Processor<T> get( Class<T> key, Class<Processor<T>> value ) {
return value.cast(values.get(key));
}
}
它会创建一个处理器地图并公开&#39; get&#39;方法。我遇到的问题是这是无效的,javac抱怨:
Processor<Integer>.class
我最后做了蛮力演员:
Processor<T> processor = (Processor<T>)(values.get(key));
但它显然给了我未经检查的投射警告。 我怎么能绕过这个警告?我想知道整个方法是不是很好,并且有更优雅的方式来设计它。
答案 0 :(得分:4)
在您的情况下,您可以将其更改为:
public <T> Processor<T> get(
Class<T> key, Class<? extends Processor<T>> value) {
// ^^^^^^^^^
return value.cast(values.get(key));
}
让你这样做:
Processor<Integer> ap = t.get(Integer.class, AProcessor.class);
或者:
public <T, P extends Processor<T>> P get(
// ^^^^^^^^^^^^^^^^^^^^^^
Class<T> key, Class<P> value) {
return value.cast(values.get(key));
}
让你这样做:
AProcessor ap = t.get(Integer.class, AProcessor.class);
这些工作是因为每个处理器都有一个子类。
javac抱怨,因为像Processor<Integer>
这样的通用类型是类型而不是类。只有Processor
是类,因此只有Processor.class
。
否则,您可以禁止警告:
public <T> Processor<T> get(Class<T> key) {
@SuppressWarnings("unchecked")
final Processor<T> p = (Processor<T>) values.get(key);
return p;
}
这种未经检查的演员设计在大多数情况下都很好,但它会因泛型类型而分解。如果您有例如一个Processor<List<String>>
,您只能将其检索为Processor<List>
。这是因为List.class
只有Class<List>
。
换句话说,假设有一个:
class CProcessor extends Processor<List<String>> {
public void process(List<String> foo) {}
}
t.values.put(List.class, new CProcessor());
// oops, we lost the type of the List
Processor<List> c1 = t.get(List.class);
// and this doesn't compile
Processor<List<String>> c2 = t.get(List.class);
然后假设有一个:
class DProcessor extends Processor<List<Float>> {
public void process(List<Float> foo) {}
}
// oops, we just replaced CProcessor
t.values.put(List.class, new DProcessor());
在这种情况下,你可以使用像番石榴TypeToken
而不是Class
这样的东西,它可以让你拥有new TypeToken<List<String>>() {}
和new TypeToken<List<Float>>() {}
。
答案 1 :(得分:2)
我认为有一种方法可以解决这种情况下未经检查的强制转换的需要,因为内部Map
的类型必须足够广泛才能处理不同的处理器。您实际上在这里进行运行时类型检查,Java泛型是一种仅编译时的机制。
在这种情况下,演员阵容很好:您可以完全控制进入该地图的对象,因此您可以确保它是正确的。如果需要,请使用注释来禁止编译器警告。
您可能希望处理器接口具有Class<T> getTargetClass()
,以便您可以在运行时测试处理器适用的类。这样可以更容易地对进入地图的处理器进行运行时验证,以确保它们是正确的。
答案 2 :(得分:0)
以下是我认为您通过匿名和半功能代码尝试实现的示例:
// simple parametrized functional interface
interface Processor<T extends Number> {
void process();
}
// definition of the actual process - "MyProcess" to avoid name clashes
abstract class MyProcess<T> implements Runnable {
protected T t;
MyProcess(T t) {
this.t= t;
}
}
// factory class to get Processor instances
class ProcessorFactory {
static <T extends Number>Processor<?> getProcessor(final MyProcess<T> process) {
return new Processor<T>() {
@Override
public void process() {
process.run();
}
};
}
}
然后,在main
方法某处...
ProcessorFactory.getProcessor(new MyProcess<Long>(42l) {
@Override
public void run() {
System.out.printf("%s: %d%n", t.getClass().getSimpleName(), t);
}
})
.process();
ProcessorFactory.getProcessor(new MyProcess<Integer>(42) {
@Override
public void run() {
System.out.printf("%s: %d%n", t.getClass().getSimpleName(), t);
}
})
.process();
<强>输出强>
Long: 42
Integer: 42
<强>评论强>
MyProcess
的同时使用“匿名化”,并且在工厂中扩展Processor
时更具争议性。