当前位置: 首页 > 知识库问答 >
问题:

Java泛型通配符捕获警告

班承恩
2023-03-14

下面的SCCE显示了实现接口标记的两个类(B和C)。对于实现Marker的每个类,都有一个实现泛型处理程序接口的对应类(B_处理程序、C_处理程序)。映射用于关联Pair的类类型。其次是它的关联处理程序。代码按预期执行;但是,我得到一个编译时警告:

警告:[未检查]未检查的强制转换处理程序h1=(处理程序)(dispatch.get(p1.second.get类()));需要:处理程序找到:处理程序,其中CAP#1是一个新鲜的类型变量:CAP#1扩展标记从捕获?扩展标记

除了@SuppressWarnings(value="uncheck")之外,解决这个问题的最干净的方法是什么?

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个答案

武骁
2023-03-14

有几个问题。

首先,您的映射无法表示每个键及其值之间的类型关系。所以如果你通过一个

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

public Pair<A, ? extends Marker> getTarget()
{
    A a = new A();
    C c = new C();
    return new Pair<>(a, c);
}

函数的最后一部分基本上是使用p1对自身进行操作,因此我们需要使用捕获助手来“捕获” p1类型中的code>转换为一个有用的类型变量,用于我们需要执行的操作。

但是,在本例中,这更复杂,因为您使用的是。getClass()<代码>foo。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);
}

仉昂熙
2023-03-14

考虑下面的例子:

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<? 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。这是不安全的,也是您收到此警告的原因。因此,无法从处理程序强制转换

 类似资料:
  • 问题内容: 阅读Java在线教程,我对通配符捕获一无所知。例如: 为什么编译器无法保留分配的安全性?它知道,例如,通过执行带有整数列表的方法,它可以从i.get中获取一个整数值。因此,它尝试将索引0处的Integer值设置为相同的Integer列表(i)。那么,怎么了?为什么要编写通配符助手? 问题答案: 为什么编译器无法保留分配的安全性? 编译器不知道 任何 关于类型中的元素通过定义。通配符并

  • 在阅读Java在线教程时,我对通配符捕获一无所知。例如:

  • 我得到以下编译错误: 当我编译(在Eclipse Juno中使用JDK 1.7.0)以下代码时: null 这样我就可以测试我所有的排序实现并测试它们。我想将结果与Java的排序实现进行比较,所以我也在编写这个接口的实现,它在内部只调用Java的排序方法。这就是我面对问题的地方。

  • 在这个问题中,我看到我可以使用帮助方法将通配符泛型“捕获”到类型T中以执行类型安全操作,如下所示: 但是当我尝试使用关键字执行此操作时,它不起作用: 我得到以下错误: 当使用扩展关键字时,有没有办法捕获通配符? 我的背景是,我有一个扩展给定类a的类列表,每个类都有一个不同的泛型参数T。对于每个类,我想获得对其T类的引用,我试图安全地进行类型转换。

  • 问题内容: 我对Java中的通用通配符有两个疑问: 和之间有什么区别? 什么是有界通配符,什么是无界通配符? 问题答案: 在你的第一个问题中,并且是有界通配符的示例。无限制的通配符看起来像,基本上就是<? extends Object>。宽松地表示泛型可以是任何类型。有界通配符(或)通过说它必须扩展特定类型(称为上限)或必须是特定类型的祖先(称为下限)来对类型进行限制。

  • 所以我在阅读泛型以重新熟悉这些概念,尤其是在涉及通配符的地方,因为我很少使用或遇到通配符。从我的阅读中,我不明白他们为什么使用通配符。下面是我经常遇到的一个例子。 你为什么不这样写: oracle网站上的另一个示例: 为什么这不是写成 我错过什么了吗?