当前位置: 首页 > 面试题库 >

了解Java中的捕获类型(符号'?')

夏志国
2023-03-14
问题内容

什么是?。它与Java编译器的实现细节有关还是JLS中定义的类型有关?

例如,

public interface RecipientTypeVisitor<ReturnType> {
    public ReturnType visit(RecipientSetType t);
}

public class RecipientSetType extends RecipientType{

    public Integer accept(RecipientTypeVisitor<?> visitor){ //Error:
        return visitor.visit(this);             //Cannot convert capture of #1 to Integer
    }
}

但是,如果我们这样写:

public interface RecipientTypeVisitor<ReturnType> {
    public ReturnType visit(RecipientSetType t);
}

public class RecipientSetType extends RecipientType{

    public Object accept(RecipientTypeVisitor<?> visitor){ //Ok:
        return visitor.visit(this);             //this implies tha the capture of #1 is 
                                     //a subtype of Object as any refrence type in Java.
    }
}

这就是我能说的captured type。那到底是什么呢?


问题答案:

捕获通配符类型是编译器使用的一种类型,它在一个特定位置代表通配符类型的特定实例的类型。

示例:以具有两个通配符参数的方法为例void m(Ex<?> e1, Ex<?> e2)。声明的类型e1e2被写入完全相同,Ex<?>。但是e1e2可能具有不同且不兼容的运行时类型。

即使以相同的方式编写类型,类型检查器也不能认为类型相等。因此,在编译期间,e1和的类型参数将e2被赋予特定的类型,并在每个使用它们的地方使用新的类型。这些新类型称为其声明类型的
捕获

捕获通配符是未知的,但是是普通的和具体的类型。可以与其他类型相同的方式使用。

可以在JLS中找到对此的技术描述:

5.1.10。捕获转化

让G命名具有n个类型参数A1,…,An并具有相应范围U1,…,Un的通用类型声明(第8.1.2节,第9.1.2节)。

存在从参数化类型G(第4.5节)到参数化类型G的捕获转换,其中对于1≤i≤n:

  • 如果Ti是形式为?的通配符类型参数(第4.5.1节),则Si是新鲜类型变量,其上限为Ui [A1:= S1,…,An:=
    Sn],下限为空类型(第4.1节)。


我们可以将其应用于您的示例:

public Integer accept(RecipientTypeVisitor<?> visitor){ //Error:
    return visitor.visit(this);             //Cannot convert capture of #1 to Integer
}

RecipientTypeVisitor在编译期间引入了对type参数的捕获。捕获的类型参数是具体的,但完全未知(JLS将此称为“新类型变量”),并且肯定不能转换为Integer

无法直接表示捕获通配符类型,因此您不能声明该类型的变量或对其进行过多处理。但是,您可以通过以其为参数调用泛型方法来间接获得其名称。我引用的JLS部分对此有一个很好的示例:

public static void reverse(List<?> list) { rev(list); }
private static <T> void rev(List<T> list) {
    List<T> tmp = new ArrayList<T>(list);
    for (int i = 0; i < list.size(); i++) {
        list.set(i, tmp.get(list.size() - i - 1));
    }
}


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

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

  • 问题内容: 我有一个通用类,说: 在此类的方法内部,我想测试T的类型,例如: 我怎样才能做到这一点 ? 谢谢你的帮助 问题答案: 通常,由于类型擦除,您不能这样做。有关更多详细信息,请参见Angelika Langer的Java泛型常见问题解答 。 您 可以 做的是将a传递给构造函数,然后检查: 请注意,尽管如此,Java仍不允许将原语用于类型参数,因此实在不行。

  • 问题内容: 我不确定以下代码中的最后一条语句为何非法。应该是的子类型,那么为什么不能将其分配给? 问题答案: 关键是指向 某种 类型的列表,但是编译器不知道该类型是什么,因此它不 知道 向其添加类型是否有效。举个例子,这也是一件好事- 您将向最初创建的用于保存字符串列表的对象添加一个。当然,这些信息在Java执行时会丢失-但是编译器会尽力确保您的安全。 见Java泛型常见问题解答了 很多 的更多信

  • 问题内容: 我正在与一起工作,并且在更换单元格工厂时需要通过一个 我所在的班级是A,但我不知道如何使用“捕获”? 我试图创造 但IDEA显示警告 我尝试使用特定的类(例如String)代替“?” 同样,但没有任何帮助。 谁能向我解释如何使用此方法? 谢谢。 编辑: 我收集了一点点信息。该的应该是,但是,TreeTableColumn是我与原始类型创建工作(在库)。 使用原始类型回叫有效。但是还有其

  • 下面的SCCE显示了实现接口标记的两个类(B和C)。对于实现Marker的每个类,都有一个实现泛型处理程序接口的对应类(B_处理程序、C_处理程序)。映射用于关联Pair的类类型。其次是它的关联处理程序。代码按预期执行;但是,我得到一个编译时警告: 警告:[未检查]未检查的强制转换处理程序h1=(处理程序)(dispatch.get(p1.second.get类()));需要:处理程序找到:处理程