下面的SCCE显示了两个实现接口标记的类(B和C)。对于实现标记的每个类,都有一个相应的类实现泛型Handler接口(B_Handler,C_Handler)。映射用于将Pair.second的类类型与其关联的Handler相关联。代码按预期执行;但是,我得到一个编译时警告:
警告:未经检查的未检查的强制转换处理程序h1 = ( Handler ) (dispatch.get(p1.second.getClass();必需:处理程序查找: CAP#1是一个新类型变量的处理程序: CAP#1从捕获?扩展标记
除了@SuppressWarnings(value = "unchecked")之外,最干净的解决方法是什么?
package genericpair;
import java.util.HashMap;
import java.util.Map;
import javax.swing.SwingUtilities;
public class GenericPair
{
public class A
{
}
public interface Marker
{
}
public class B implements Marker
{
}
public class C implements Marker
{
}
public Pair<A, Marker> getTarget()
{
A a = new A();
C c = new C();
return new Pair<>(a, c);
}
public interface Handler<T extends Marker>
{
void handle(Pair<A, T> target);
}
public class B_Handler implements Handler<B>
{
@Override
public void handle(Pair<A, B> target)
{
System.out.println("B");
}
}
public class C_Handler implements Handler<C>
{
@Override
public void handle(Pair<A, C> target)
{
System.out.println("C");
}
}
public class Pair<F, S>
{
public final F first;
public final S second;
public Pair(F first, S second)
{
this.first = first;
this.second = second;
}
}
private void executeSCCE()
{
// register a handler for each Marker type
Map<Class, Handler<? extends Marker>> dispatch = new HashMap<>();
dispatch.put(B.class, new B_Handler());
dispatch.put(C.class, new C_Handler());
// get a target (e.g., Pair<A,C>)
Pair<A, Marker> p1 = getTarget();
// select handler based on the class type of the second parameter
Handler<Marker> h1 = (Handler<Marker>) (dispatch.get(p1.second.getClass()));
h1.handle(p1);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> new GenericPair().executeSCCE());
}
}发布于 2016-10-30 09:44:47
有几个问题。
首先,您的Map无法表示每个键与其值之间的类型关系。因此,如果您将一个Class<T>传递给dispatch.get(),您只能得到一个Handler<? extends Marker>,而不是Handler<T>。实际上,没有哪种类型可以让dispatch使其工作。相反,您必须创建一个包装类来通过它的API来执行这种关系:
public class ClassToHandlerMap
{
private final Map<Class<?>, Handler<?>> map = new HashMap<>();
public <T extends Marker> void put(Class<T> clazz, Handler<T> handler) {
map.put(clazz, handler);
}
@SuppressWarnings("unchecked")
public <T extends Marker> Handler<T> get(Class<T> clazz) {
return (Handler<T>)map.get(clazz);
}
}请注意,您仍然必须在这个类中取消未经检查的警告,但至少在这里您知道它是正确的,这是基于如何允许将事情放到映射中的。未检查的强制转换只是这个类的用户不需要知道的实现细节。
第二个问题是,getTarget()可能应该返回Pair<A, ? extends Marker>而不是Pair<A, Marker>。您从来没有Handler的Marker;相反,您有特定类型的Marker的Handlers。因此,您只使用特定类型的Pairs也是有意义的。
public Pair<A, ? extends Marker> getTarget()
{
A a = new A();
C c = new C();
return new Pair<>(a, c);
}函数的最后一部分基本上是使用p1对自身进行操作,因此我们需要使用捕获助手将p1类型中的?“捕获”为我们需要做的有用的类型变量。
但是,在本例中,这要复杂得多,因为您使用的是.getClass()。foo.getClass()具有Class<? extends |X|>类型,其中|X|是foo编译时类型的擦除。因此,无论p1的类型是Pair<A, ?>还是Pair<A, T>,p1.second.getClass()仍然会返回Class<? extends Marker>类型。因此,仅在?上捕获Pair<A, ?>是不够的;相反,我们应该在返回.getClass()时捕获?。
@SuppressWarnings("unchecked")
private static <T extends Marker> void captureHelper(Class<T> clazz,
Pair<A, ? extends Marker> p, ClassToHandlerMap dispatch) {
Pair<A, T> p1 = (Pair<A, T>)p;
Handler<T> h1 = dispatch.get(clazz);
h1.handle(p1);
}不幸的是,我们也将不得不在这里做一个未经检查的广播。由于.getClass()的特殊返回类型,我们无法连接.getClass()的返回类型和调用它的表达式。而且我们不能使用像.cast()这样的运行时转换来在参数化类型之间进行转换(如果我们将给定类的一个实例作为参数,我们可以使用.cast()来消除未检查的强制转换,但这里没有)。在某些边缘情况下,这可能是不正确的,但只要您始终使用Pair,而第二种类型的参数是最后的实现类,那么它就应该是正确的。
最后,主要方法如下所示:
private void executeSCCE()
{
// register a handler for each Marker type
ClassToHandlerMap dispatch = new ClassToHandlerMap();
dispatch.put(B.class, new B_Handler());
dispatch.put(C.class, new C_Handler());
// get a target (e.g., Pair<A,C>)
Pair<A, ? extends Marker> p1 = getTarget();
// select handler based on the class type of the second parameter
captureHelper(p1.second.getClass(), p1, dispatch);
}发布于 2016-10-28 18:52:16
请考虑以下示例:
List<? extends List> test1 = new ArrayList<>();
List<List> test2 = (List<List>) test1;在这里我们得到警告:
warning: [unchecked] unchecked cast
List<List> test2 = (List<List>) test1;
^
required: List<List>
found: List<CAP#1>
where CAP#1 is a fresh type-variable:
CAP#1 extends List from capture of ? extends List之所以会出现这种情况,是因为无法确保List<List>的泛型约束与List<? extends List>匹配。假设我们将此示例重写为以下内容:
List<? extends List> test1 = new ArrayList<ArrayList>();
List<List> test2 = (List<List>) test1;
test1.add(new LinkedList<>());//ERROR no suitable method found for add(LinkedList<Object>)
test2.add(new LinkedList<>());//Will work fine!!在这里,更明显的是,最初的合同被打破了。定义为包含ArrayList的列表现在包含一个LinkedList。这是不安全的,这就是为什么你要收到这个警告。因此,无法安全地从Handler<? extends Marker>转换到Handler<Marker>。
https://stackoverflow.com/questions/40310984
复制相似问题