?
указывает местозаполнитель, значение которого вас не интересует (подстановочный знак):
HashMap<?, ?> foo = new HashMap<Integer, String>();
А так как ?
является подстановочным знаком, вы можете пропустить его и при этом получить тот же результат:
HashMap foo = new HashMap<Integer, String>();
Но они могут использоваться для указания или подмножества используемых обобщений.В этом примере первый универсальный объект должен реализовывать интерфейс Serializable.
// would fail because HttpServletRequest does not implement Serializable
HashMap<? extends Serializable, ?> foo = new HashMap<HttpServletRequest, String>();
Но всегда лучше использовать конкретные классы вместо этих подстановочных знаков.Вы должны использовать ?
, только если знаете, что делаете :)