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

Java泛型:通配符vs类型参数?

壤驷英叡
2023-03-14

我正在刷新我关于Java泛型的知识。所以我转向甲骨文的优秀教程...并开始为我的同事准备一个演示文稿。我在教程中遇到了通配符部分,上面写着:

考虑以下方法,打印列表:

public static void printList(List<Object> list) {
...

printList的目标是打印任何类型的列表,但它无法实现该目标-它只打印对象实例的列表;无法打印列表

public static void printList(List<?> list) {

我明白那个列表

static <E> void printObjects(List<E> list) {
    for (E e : list) {
        System.out.println(e.toString());
    }
}
...
    List<Object> objects = Arrays.<Object>asList("1", "two");
    printObjects(objects);
    List<Integer> integers = Arrays.asList(3, 4);
    printObjects(integers);

猜猜看是什么;使用<代码>列表

长话短说:至少教程指出需要通配符来解决这个问题;但如图所示,它也可以通过这种方式解决。那么,我错过了什么?!

(旁注:用Java7测试;所以这可能是Java5、Java6的问题;但另一方面,Oracle似乎在更新其教程方面做得很好)


共有3个答案

扈德容
2023-03-14

两种解决方案实际上是相同的,只是在第二种解决方案中,您命名了通配符。当您希望在签名中多次使用通配符,但希望确保两者引用相同的类型时,这会很方便:

static <E> void printObjects(List<E> list, PrintFormat<E> format) {
蒋哲
2023-03-14

从技术上讲,两者之间没有区别

<E> void printObjects(List<E> list) {

void printList(List<?> list) {

>

  • 当您声明一个类型参数并且只使用它一次时,它本质上变成了一个通配符参数。
  • 另一方面,如果您多次使用它,差异就会变得很明显。

    <E> void printObjectsExceptOne(List<E> list, E object) {
    

    完全不同于

    void printObjects(List<?> list, Object object) {
    

    您可能会看到,第一种情况强制两种类型相同。第二种情况没有限制。

    因此,如果只使用一次类型参数,那么命名它甚至没有意义。这就是为什么java架构师发明了所谓的通配符参数(最有可能)。

    通配符参数避免了不必要的代码膨胀,并使代码更具可读性。如果需要两个,则必须回到类型参数的常规语法。

    希望这有帮助。

  • 路欣荣
    2023-03-14
    匿名用户

    您使用泛型方法的方法严格来说比带有通配符的版本更强大,所以是的,您的方法也是可能的。但是,教程并没有说明使用通配符是唯一可能的解决方案,所以教程也是正确的。

    与泛型方法相比,通配符带来的好处是:由于非泛型方法更容易掌握,因此您必须编写更少的代码,并且接口更“干净”。

    为什么泛型方法比通配符方法更强大:您给参数一个可以引用的名称。例如,考虑一个方法,它删除列表的第一个元素并将其添加到列表的后面。使用泛型参数,我们可以执行以下操作:

    static <T> boolean rotateOneElement(List<T> l){
        return l.add(l.remove(0));
    }
    

    使用通配符时,这是不可能的,因为l.remove(0)将返回capture-1-of- ,但l.add需要捕获-2-of- 。一、 例如,编译器无法推断删除的结果与添加的预期类型相同。这与第一个示例相反,在第一个示例中,编译器可以推断两者是相同的类型T。此代码无法编译:

    static boolean rotateOneElement(List<?> l){
        return l.add(l.remove(0)); //ERROR!
    }
    

    那么,如果你想要一个带有通配符的rotateOneElement方法,你能做些什么呢?因为它比通用解决方案更容易使用?答案很简单:让通配符方法调用泛型方法,那么它就会工作:

    // Private implementation
    private static <T> boolean rotateOneElementImpl(List<T> l){
        return l.add(l.remove(0));
    }
    
    //Public interface
    static void rotateOneElement(List<?> l){
         rotateOneElementImpl(l);
    }
    

    标准库在许多地方使用此技巧。其中之一是IIRC,收藏。Java语言

     类似资料:
    • 这2个功能有什么区别? 我看到了相同的输出。

    • 问题内容: 我正在刷新有关Java泛型的知识。因此,我转向了Oracle的优秀教程……并开始为我的同事编写一个演示文稿。我在本教程中遇到了有关通配符的部分,内容为: 考虑以下方法,printList: printList的目标是打印任何类型的列表,但未能实现该目标- 它仅打印Object实例的列表;它不能打印,,,等等,因为它们不是的亚型。要编写通用的printList方法,请使用: 我知道那是行

    • 在了解Java泛型的过程中,我遇到了以下问题: 假设我有下面的方法来添加列表的元素,只限于包含数字的列表。 但是这段代码和这段代码有什么不同: 它们都按预期编译和执行。这两者之间有什么区别?除了语法之外?什么时候我更喜欢使用通配符而不是前者? 是的,使用通配符方法,我不能在列表中添加除null之外的新元素,否则它将无法编译。除此之外呢?

    • 所以我看了官方的java教程,https://docs.oracle.com/javase/tutorial/java/generics/index.html,也搜索了stackoverflow,结果发现使用

    • 问题内容: 我试图理解Java泛型,它们似乎很难理解。例如,这很好… …就是这个… … 还有这个 … …但是不能编译: 有人可以用简单的语言解释发生了什么吗? 问题答案: 对于泛型类型,主要要了解的是它们不是协变的。 因此,尽管您可以这样做: 以下内容将无法编译: 这是为了避免您绕过通用类型的情况: 因此,一一讲解您的示例 1个 您的通用方法采用a ,而您采用;(基本上是)。可以分配给类型,并且编

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