首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java Generics通配符捕获警告

Java Generics通配符捕获警告
EN

Stack Overflow用户
提问于 2016-10-28 18:17:51
回答 2查看 1K关注 0票数 3

下面的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")之外,最干净的解决方法是什么?

代码语言:javascript
复制
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());
    }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-10-30 09:44:47

有几个问题。

首先,您的Map无法表示每个键与其值之间的类型关系。因此,如果您将一个Class<T>传递给dispatch.get(),您只能得到一个Handler<? extends Marker>,而不是Handler<T>。实际上,没有哪种类型可以让dispatch使其工作。相反,您必须创建一个包装类来通过它的API来执行这种关系:

代码语言:javascript
复制
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>。您从来没有HandlerMarker;相反,您有特定类型的MarkerHandlers。因此,您只使用特定类型的Pairs也是有意义的。

代码语言:javascript
复制
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()时捕获?

代码语言:javascript
复制
@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,而第二种类型的参数是最后的实现类,那么它就应该是正确的。

最后,主要方法如下所示:

代码语言:javascript
复制
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);
}
票数 1
EN

Stack Overflow用户

发布于 2016-10-28 18:52:16

请考虑以下示例:

代码语言:javascript
复制
List<? extends List> test1 = new ArrayList<>();
List<List> test2 = (List<List>) test1;

在这里我们得到警告:

代码语言:javascript
复制
 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>匹配。假设我们将此示例重写为以下内容:

代码语言:javascript
复制
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>

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40310984

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档