Java Generics通配符捕获警告

时间:2016-10-28 18:17:52

标签: java generics

下面的SCCE显示了实现接口标记的2个类(B和C)。对于实现Marker的每个类,都有一个实现泛型Handler接口(B_Handler,C_Handler)的相应类。映射用于将Pair.second的Class类型与其关联的Handler相关联。代码按预期执行;但是,我得到了一个编译时警告:

警告:[未选中]未经检查的强制转换         处理程序h1 =(处理程序)(dispatch.get(p1.second.getClass()));   必需:处理程序   发现:处理程序   其中CAP#1是一个新的类型变量:     CAP#1扩展了Marker的捕获?扩展标记

除了@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());
    }
}

2 个答案:

答案 0 :(得分:4)

考虑以下示例:

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>

答案 1 :(得分:1)

有几个问题。

首先,您的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的{​​{1}};相反,您有Marker个特定类型的Handler。因此,您只使用Marker特定类型的Pair也是有道理的。

Marker

你的函数的最后一部分主要是使用public Pair<A, ? extends Marker> getTarget() { A a = new A(); C c = new C(); return new Pair<>(a, c); } 来操作它自己,所以我们需要使用一个捕获帮助器来捕获&#34; 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