Java中的“<?>”符号是什么含义?

7
这段代码取自www.JavaPractices.com,其中<?>这个token的含义是什么?如果我将其替换为用于泛型类型的更常见的<T>,则无法编译。(错误:T无法解析为类型。)为什么会这样?
// <?> occurs 3 times in the entire program.  When it is replaced with <T> the
// program no longer compiles.

void activateAlarmThenStop()
{
    Runnable myPeriodicTask = new PeriodicTask();
    ScheduledFuture<?> soundAlarmFuture = 
        this.executorService.scheduleWithFixedDelay(myPeriodicTask, 
                                          startT, 
                                          period, 
                                          TimeUnit.SECONDS
                                         );
    Runnable stopAlarm = new StopAlarmTask(soundAlarmFuture);
    this.executorService.schedule(stopAlarm, stopT, TimeUnit.SECONDS);
}

private final class StopAlarmTask implements Runnable 
{
    StopAlarmTask(ScheduledFuture<?> aSchedFuture)
    {
        fSchedFuture = aSchedFuture;
    }

    public void run() 
    {
        CConsole.pw.println("Stopping alarm.");
        fSchedFuture.cancel(doNotInterruptIfRunningFlag);

        executorService.shutdown();
    }
    private ScheduledFuture<?> fSchedFuture;
}

编辑:当我们使用像<T>这样的通用类型标记时,它必须出现在类声明中。但是在这里,类声明中没有<T><?>,但它仍然可以编译和运行。

8个回答

6
它无法编译,因为您的类不是泛型(也没有任何方法是泛型)。在这个特定的例子中,joker(?)表示ScheduledFuture可以由任何东西参数化。
有时,如果您在内部使用另一个泛型类并且不知道将使用的确切类型,则没有必要使整个类成为泛型。在这个例子中,您有三个选项:
1. 使StopAlarmTask成为泛型(在这种情况下没有意义) 2. 在ScheduledFuture中使用具体类型,但那时只会有一种可能的结果类型,例如String或Integer 3. 使用通配符(< ? >)- 它允许检索FutureResult的任何内容(String,Integer,自定义类)。您还可以将可能的泛型类型范围缩小到某些子类,例如ScheduledGeneric< ? extends MyObject>或超类:ScheduledGeneric< ? super MyObject>

你的回答似乎与用于泛型类型的<T>一致。然而,问题是关于<?>的。 - H2ONaCl

2
这是使用通配符作为类型参数的示例,即泛型类型。通配符参数化类型是泛型类型的实例化,其中至少一个类型参数是通配符。通配符参数化类型的示例包括Collection<?>, List<? extends Number>, Comparator<? super String>和Pair<String,?>。
通配符参数化类型表示一组类型,包括泛型类型的具体实例化。使用的通配符类型决定了哪些具体参数化类型属于该组。

2
假设this.executorServiceScheduledExecutorService的子类型(自Java 1.5以来可用),scheduleWithFixedDelay()的返回类型为ScheduledFuture<?>。您无法将返回类型从ScheduledFuture<?>更改为ScheduledFuture<T>ScheduledFuture<Integer>或其他任何类型。但是,您可以将其更改为ScheduledFuture,因为<?>是通配符泛型类型参数,近似于原始类型以实现向后兼容性。
请参见什么是原始类型,为什么不应该使用它?,了解有关原始类型和泛型的详细讨论。

1

这是关于编程的内容,讲述了“泛型”(Generic Type)的概念。通常我们会将String、Object或其他对象设置为泛型类型,但在这里它被定义得更加普遍化。泛型类型代表着它可以“存储或持有”的值。它通常用于“集合”(Collections)中。

实际上,在两者之间并没有太大的区别——都可以接受各种类型的对象。

              <T>-is also called `formal type parameter` makes use of what ever type of object you pass in .. for instance you can do this with <T> and not with <?>

public class Box<T> {

    private T t; // T stands for "Type"

    public void add(T t) {
        this.t = t;
    }

    public T get() {
        return t;
    }
}

更多信息请参见http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdfhttp://download.oracle.com/javase/tutorial/java/generics/gentypes.html


你的回答没有描述<T>和<?>之间的区别。 - H2ONaCl
“不太有差别”并不是一个很好的回答,哈哈。 - H2ONaCl

1

方括号中的字母,如<T>,是类型参数。您可以使用class StopAlarmTask<T>来表示您正在将类型StopAlarmTask与类型T进行参数化。类型参数T将成为类型的一部分,就像构造函数参数成为新实例的一部分。

然后,每当您声明一个StopAlarmTask时,您都需要提供一个类型,例如String,以填充类型参数T。然后,您可以在类体内引用该类型参数。例如,您可以定义接受T或返回T的方法,或者使用T对成员变量(如fSchedFuture)进行参数化。例如,如果您将StopAlarmTask<T>的声明参数化为StopAlarmTask<String>,那么String将被捕获为T,并且您在StopAlarmTask中使用T时,它将作为String

然而,在您列出的代码中,StopAlarmTask没有类型参数,因此无法使用任何类型进行参数化。类体内没有可以引用的捕获类型 T

另一方面,<?>表示 "我不知道它将是哪种类型;我甚至不知道它是否是某人用于参数化StopAlarmTask的类型。"

您可以对StopAlarmTask<T>进行参数化,在这种情况下,您可以有两个变量:

private ScheduledFuture<T> fSchedFuture1;
private ScheduledFuture<?> fSchedFuture2;

第一个声明表示,ScheduledFuture 的类型参数与封闭的 StopAlarmTask 的类型参数相同。例如,StopAlarmTask<String> 将使 fSchedFuture1 成为 ScheduledFuture<String>。第二个声明表示,即使我们知道封闭的 StopAlarmTask 的类型参数,我们也不知道 ScheduledFuture 的类型参数是什么。


0

通配符?可以容纳任何类型。如果您想要在所有方法/成员中使用相同的类型,可以使整个类成为泛型。通过编写 StopAlarmTask<T>,您可以在整个类中定义类型T

private final class StopAlarmTask<T> implements Runnable 
{
    StopAlarmTask(ScheduledFuture<T> aSchedFuture)
    {
        fSchedFuture = aSchedFuture;
    }

    public void run() 
    { /* */ }

    private ScheduledFuture<T> fSchedFuture;
}

您正在描述 <T>,但问题是关于 <?>。 - H2ONaCl

0

StopAlarmTask 不是一个通用类型。

在下面的例子中,你不会认为 Foo 是一个通用类型。

class Foo
{
    Foo(int i)
    { }

    doStuff(List<Integer> numbers)
    { }
}

StopAlarmTaskconstructor 使用了一个泛型参数,并不意味着这个 class 是泛型的,就像 doStuff() 并不会使 Foo 成为泛型。

使用 <?> 以一种泛型的方式 "引用" 泛型类型的声明,也就是说,没有具体性。在 StopAlarmTask 中,它恰好是一个构造函数参数。这是对泛型类型的 "应用" 而不是泛型类型的声明,因为它只是一个参数声明。

换句话说,简短的答案是方法中的参数。

StopAlarmTask(ScheduledFuture<?> aSchedFuture)
{ ... }

适用于所有的ScheduledFuture<T>对象实例,其中T是任意类型。

以下是关于泛型的更多背景。

使用<T>或者<E> 或其他来声明通用类型ScheduledFuture<T>。具体来说,在ScheduledFuture<>的声明中不会使用<?> ,因为惯例是使用单个大写字母。

请注意,如果将以下测试代码输入编译器,则第一个类将编译通过,而第二个类将无法编译,因此可以说使用字母的惯例实际上是一种低调的说法。

class TGeneric1<E> {
    List<E> list = new ArrayList<E>();
    TGeneric1(E value) {
        this.list.add(value);
    }
    E getHead() {
        return this.list.get(0);
    }
}

class TGeneric2<?> {
    List<?> list = new ArrayList<?>();
    TGeneric2(? value) {
        this.list.add(value);
    }
    ? getHead() {
        return this.list.get(0);
    }
}

在下面的测试代码中,没有单个字母的限制,因此以下内容也是正确的。
class TGeneric1<EE> {
    List<EE> list = new ArrayList<EE>();
    TGeneric1(EE value) {
        this.list.add(value);
    }
    EE getHead() {
        return this.list.get(0);
    }
}

0

它可以接受任何参数,如Object、String、Integer等等。

现在,当您计划使用泛型时,必须在尖括号内提供类型。

编辑:

<?> 严格适用于集合。

<T> 用作普通类的类型或模板。

你正在描述 <T>,但问题是关于 <?>。 - H2ONaCl

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