简体   繁体   中英

What is the meaning of the <?> token in Java?

What is the meaning of the <?> token in this code copied from www.JavaPractices.com? When I replace it with the more conventional looking <T> used for generic types, it fails to compile. (Error: T cannot be resolved to a type.) Why?

// <?> 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;
}

Edit: Of course when we use generic type tokens like <T> , it has to appear in the class declaration. Here there is no <T> nor <?> in the class declaration but it still compiles and runs properly.

It fails to compile, because your class is not generic (nor any of your methods). In this particular example joker (?) means that ScheduledFuture may be parametrized by anything.

Sometimes, there is no sense to make the whole class generic if you use another generic class inside and you don't know the exact type that will be used. In this example you had three options:

  1. make StopAlarmTask generic (there is no sense in this case)
  2. use concrete type in ScheduledFuture, but then it would be only one possible result type, for example String or Integer
  3. use wildcard ( < ? > ) - it allows to retrieve anything as a result of FutureResult (String, Integer, your custom class). You can also narrow the scope of a possible generic type into some subclasses, for example ScheduledGeneric< ? extends MyObject > ScheduledGeneric< ? extends MyObject > or into superclasses: ScheduledGeneric< ? super MyObject > ScheduledGeneric< ? super MyObject >

This is an example of using wildcard in a type argument. ie a generic type. A wildcard parameterized type is an instantiation of a generic type where at least one type argument is a wildcard. Examples of wildcard parameterized types are Collection<?> , List<? extends Number> List<? extends Number> , Comparator<? super String> Comparator<? super String> and Pair<String,?> .

A wildcard parameterized type denotes a family of types comprising concrete instantiations of a generic type. The kind of the wildcard being used determines which concrete parameterized types belong to the family.

That is Generic Type ... usually we set String, Object, or any other objects as generic types... but here they make it general. and generic type stands for the value it can store or hold . its generally used in Collections ..

well there is no much diff between the both.. - takes all kind of objects

              <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;
    }
}

for more see http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf and http://download.oracle.com/javase/tutorial/java/generics/gentypes.html

A letter in brackets like <T> would be a type parameter. You would say class StopAlarmTask<T> to mean that you are parameterizing the type StopAlarmTask with the type T . The type parameter T would become a part of the type, sort of like a constructor argument becomes part of a new instance.

Then, whenever you declared a StopAlarmTask , you would provide a type, eg String , to fill in the type parameter T . You could then refer to that type parameter within the class body. For example, you could define methods which take a T or return a T , or parameterize member variables like fSchedFuture with T . For example, if you parameterized a declaration of StopAlarmTask<T> as StopAlarmTask<String> , then String would be captured as T and wherever you used T within that StopAlarmTask it would act as String .

However, in the code you have listed, StopAlarmTask does not have a type parameter and cannot be parameterized with any type. There is no captured type to refer to as T within the class body.

On the other hand, <?> means "I don't know which type it will be; I don't even know that it will be the type that someone has used to parameterize StopAlarmTask ."

You could have parameterized StopAlarmTask<T> , and in that case you could have two variables:

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

The first declaration says that the type parameter of the ScheduledFuture is the same as the type parameter of the enclosing StopAlarmTask . Eg StopAlarmTask<String> would make fSchedFuture1 into a ScheduledFuture<String> . The second declaration says that we don't know what the type parameter of the ScheduledFuture is, even if we know the type parameter of the enclosing StopAlarmTask .

Assuming this.executorService is a subtype of ScheduledExecutorService (available since Java 1.5), the return type of scheduleWithFixedDelay() is ScheduledFuture<?> . You can't change the return type from ScheduledFuture<?> to ScheduledFuture<T> , ScheduledFuture<Integer> or anything else for that matter. You could, however, change it to just ScheduledFuture since <?> is a wildcard generic type parameter that approximates a raw type for backward compatibility.

See What is a raw type and why shouldn't we use it? for a good discussion on raw types and generics.

It can take any parameter like Object,String,Integer.... and so on

Now when you plan to use generics then you have to provide the type inside the angel bracket.

EDIT:

<?> is strictly appliable to Collections. <T> used as type or template for your normal class

The joker ? can hold any type. If you want to use the same type for all methods/members you can make the whole class generic. By writing StopAlarmTask<T> you define the type T in the whole class.

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

    public void run() 
    { /* */ }

    private ScheduledFuture<T> fSchedFuture;
}

StopAlarmTask is not a generic type.

In the following example, you would not think Foo is a generic type.

class Foo
{
    Foo(int i)
    { }

    doStuff(List<Integer> numbers)
    { }
}

The fact that the constructor of StopAlarmTask uses a generic parameter does not make the class generic any more than doStuff() makes Foo generic.

Use <?> to "refer to" the the declaration of a generic type in a generic way , that is, without specificity. In StopAlarmTask it just happens to be a constructor parameter. It is an "employment of" a generic type and not a declaration of a generic type because it is "merely" a parameter declaration.

In other words, the short answer is that the parameter in the method

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

is applicable for all objects that are instances of ScheduledFuture<T> for all T.

The following is further background on generics.

Use <T> or <E> or whatever to declare the generic type ScheduledFuture<T> . Specifically, <?> would not be used in the declaration of ScheduledFuture<> because the convention is to use a single uppercase letter.

Note that the following test code, if fed to a compiler, will show that the first class compiles but the second does not, so to say that there is a convention to use a letter would actually be an understatement.

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);
    }
}

Illustrated in the following test code, there is no single letter constraint, so the following is also correct.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM