Java中使用"?"的目的是什么?

6

我想了解在Java泛型中?的用法。通过学习占位符T和通配符?,我对?产生了疑问,查阅了几个网站/页面和书籍,但未能理解它。因此,我创建了下面的类来研究它们之间的区别。

import java.util.List;

public class Generics2 {

    public <T> void method1(List<T> list){
        System.out.println(list);
    }

    public <T extends Number> void method2(List<T> list){
        System.out.println(list);
    }

    /*public <T super Integer> void method3(List<T> list){

    }*///super does not work.

    public void method4(List<?> list){
        System.out.println(list);
    }

    public void method5(List<? extends Number> list){
        System.out.println(list);
    }

    public void method6(List<? super Integer> list){
        System.out.println(list);
    }

    public <T> void copy1(List<T> list1, List<T> list2){
        //copy elements from list1 to list2

    }//It does not span well with copy of one type of elements from list1 to other type elements in list2, where the list elements 
    //between the two are not same but are related through inheritance.

    public <T1,T2> void copy2(List<T1> list1,List<T2> list2){
        //copy elements from list1 to list2, right now we do not bother about the exceptions or errors that might generate.
    }//Our intention here is not to copy elements with relation between T1 and T2. We intend to explore the differences on T and ?

    public void copy3(List<?> list1,List<?> list2){
        //copy elements from list1 to list2, right now we do not bother about the exceptions or errors that might generate.
    }//Our intention here is not to copy elements with relation between T1 and T2. We intend to explore the differences on T

    public <T1 extends Object, T2 extends Object> void copy4(List<T1> list1, List<T2> list2){
        //copy elements from list1 to list2
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub

    }

}

在某些情况下,我的类可能会缺少几个场景,因此我写的不完整,在这种情况下,是否有人可以帮助我实现更多场景。或者我发现?在某些方面是多余的,除了提供像使用super关键字和方法签名中的返回类型较少的功能之外。
编辑:基本上我的问题是要知道引入通配符?的原因,其中一个泛型类型可以替代它。这不是如何使用?或T类型的问题。当然,了解其用途将提供一些答案。 例如,我得出的结论:
- 在某些地方,?使代码更易读,但编码不太容易。 - 它有时会减少代码膨胀。 - 我们可以使用超类,而T类型无法做到这一点。 - 限制向列表添加新的随机元素。在某些情况下,对于T类型,强制转换(没有classcastexception)是有效的。
还有其他吗?

请求管理员或版主。需要一天或两天的时间来仔细研究这些内容,因此请允许我逐个查看提供给这个问题的每个链接。 - smslce
我不知道如何删除重复的问题,但我不认为它会很快发生,如果真的发生的话。 - biziclop
3个回答

3
?的意思是:类型未指定。这意味着:如果您的代码中没有其他需要“命名”类型参数的原因,您可以/应该使用?来表达这一点。实际上,我已经询问过一段时间了,所以你可能会发现这里的答案很有帮助:Java generics: wildcard<?> vs type parameter<E>?

我阅读了提供的链接,当我看到你选择的答案时,它开始说有时候?和T之间没有区别。后来解释了占位符泛型比?通配符更可用(指由于?而导致的错误),这又回到了对?使用的问题上。还能否对“如果你的代码中没有其他原因需要你拥有一个“命名”的类型参数”这个陈述提供更多见解?这是指在实现它的整个类中都没有使用类型参数,还是只在我们需要传递某些东西的方法中没有使用类型参数? - smslce
重点是:如果你需要写“T”,那么你应该使用“T”。如果类型未知且不重要,则写“?”可以更明确地表示(但你仍然可以使用“T”而不是“?”)。 - GhostCat
你能在我编辑后的问题中添加任何东西吗? - smslce

2
当列表(或集合)的类型未知时,您可以使用无限通配符。但是,如果您想向列表中插入值(因为您只能插入null值),则永远不应该使用它。
当您想获取有关某个数据结构的信息(例如打印其内容)时,但不知道它可能包含的类型时,可以使用它。例如:
public static void printList(List<?> list) {
    for (Object elem: list)
        System.out.print(elem + " ");
    System.out.println();
}

这不就跟下面代码一样吗?public static <T extends Object> void printList(List<T> list){ for (Object elem: list) System.out.print(elem + " "); System.out.println(); } - smslce

1

有人在这里写了一个很好的回答解释了这个问题。问号基本上表示“任何类型”,但在使用接口或抽象类时非常有用。例如,

List<? extends MyAbstract> myClasses = new ArrayList<? extends MyAbstract>();

这很有用,因为你仍然可以进行类型检查,以确保放入列表中的任何内容都是MyClass的子类。如果你需要使用抽象类中的特定方法或字段,你可以为所有子类使用它,而不管具体实现如何。举个更实际的例子,我在URL路由中使用这个:
Map<String, Class<? extends IJSONService>> routes = new HashMap<String, Class<? extends IJSONService>>(){
  {
    put("default", EmployeeFinderSearch.class);
    put("", EmployeeFinderSearch.class);
    put("reports", EmployeeFinderReports.class);
    put("me", Me.class);
    put("email", SendEmail.class);
    put("image", UploadImage.class);
    put("excel", ExportExcel.class);
  }};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接