2013-02-28 2 views
3

У меня возникли проблемы с объявлением полностью общего типа для вывода метода. Вот ситуация:Объявление двойного использования подстановочных знаков в java

class A<S,T>{ 
public Callback<B<S,T>,C<S,T>> method(); 
} 

в моем коде у меня есть один из них;

A<String, ?> instance; 

Однако, когда я называю

result = instance.method() 

единственный способ сделать компилятор счастливым объявить

Callback<?, ?> result = instance.method() 

Но я бы очень хотел, чтобы быть более точным с моими генериков и объявить

Callback<B<String, ?>,C<String, ?>> result = instance.method() 

однако, так как каждый подстановочное отдельно, компилятор жалуется, что это неправильно, заявив, что он не может конвертировать из

Callback<B<String,capture#3-of ?>,C<String,capture#3-of ?>> 

Есть ли способ объявления этой ситуации правильно?

FYI, классы A, B и C - из внешних библиотек. Я попытался объявить свой экземпляр в качестве

A<String, Object> 

, но это также результат метода из четвертого класса, который я не имею никакого контроля над:

class D<T>{ 
public A<T, ?> getObject(); 
} 

D<String> start = new D<String>(); 
A<String, ?> = start.getObject(); 
... 
+2

Подпись 'D.getObject()' дает мне плохое чувство. Зачем вам * возвращать * объекты, на которые вы не можете получить никаких разумных гарантий типа? То есть, если 'D' не знает, какой тип'? 'Должен быть, как черт должен обработать вызывающий? Неудивительно, что компилятор действительно не пытается вас устраивать. «?» Должен означать «Мне все равно, какой тип это сейчас», а не «я понятия не имею, что это за тип» – millimoose

+0

Я полностью согласен. Я действительно не знаю, почему это так, но это не мой класс; это основной класс javafx. Все еще застрял tho:/ – Jocken

ответ

1

Это действительно неудобная ситуация, результат ограничений подстановки шаблонов и менее идеального типа возврата от D.getObject.

Существует несколько обходных решений, но ни один из них не симпатичен.Во-первыхи, чтобы просто сделать неконтролируемый бросок:

@SuppressWarnings("unchecked") //it's okay for the two captures not to match 
Callback<B<String, ?>, C<String, ?>> result = 
     (Callback<B<String, ?>, C<String, ?>>)instance.method(); 

EDIT: Некоторых компиляторов (более правильно) требует двойного бросания через Callback<?, ?>:

@SuppressWarnings("unchecked") //it's okay for the two captures not to match 
Callback<B<String, ?>, C<String, ?>> result = 
     (Callback<B<String, ?>, C<String, ?>>)(Callback<?, ?>)instance.method(); 

Вы также мог бы использовать вспомогательный метод - это общее решение вопросов подстановочных захвата:

static <T> void helper(A<String, T> a) { 

    Callback<B<String, T>, C<String, T>> result = a.method(); 

    //logic 
} 

... 

helper(instance); 

UPDATE:

На самом деле вы можете объединить эти два подхода, по крайней мере, изолировать уродства в один вспомогательный метод:

static <S, T> Callback<B<S, ?>, C<S, ?>> disentangleCallback(
     Callback<B<S, T>, C<S, T>> callback 
) { 
    @SuppressWarnings("unchecked") //it's okay for the two captures not to match 
    final Callback<B<S, ?>, C<S, ?>> withWidenedTypes = 
      (Callback<B<S, ?>, C<S, ?>>)(Callback<?, ?>)callback; 
    return withWidenedTypes; 
} 

, а затем использовать его как это:

Callback<B<String, ?>, C<String, ?>> result = 
     disentangleCallback(instance.method()); 

Или аналогичный метод, который просто берет A<S, T> и возвращает Callback<B<S, ?>, C<S, ?>>, вызывая method сам, затем кастинг.


Еще одна идея: если второй тип аргумента A является всегда неизвестный, это может быть проще просто удалить его и расширить тип возвращаемого method:

class A<S> { 
    public Callback<B<S, ?>, C<S, ?>> method() { ... } 
} 

Я понимаю, что было бы неудачной концессией, но имеет смысл, если T никогда не будет действительно полезным.

+0

решение 1) hard cast отвергается компилятором. Решение 2) смотрит; имеет обещание. решение 3) У меня нет контроля над классом A. – Jocken

+0

@Jocken Хорошо, посмотрим мое (исправленное) редактирование. –

+0

Я вижу, очень приятно. Это уродливо, как грех, но компилятор счастлив с двойным литьем и методом вызова компилирует. Я буду держать вас в курсе того, как это работает. – Jocken

1

Вы могли бы

Callback<? extends B<String, ?>, ? extends C<String, ?>> result 
    = instance.method(); 
+0

Я рассмотрел этот вариант, но, похоже, он уменьшает полезность «результата» - зависит от того, как он используется. –

+0

хорошее решение. Это компиляция, я дам вам знать, не закончится ли она тем, что мне нужно. – Jocken

+0

Проблема с следующим шагом: теперь невозможно удовлетворить метод 'call (B '. У меня есть экземпляр 'B ', и теперь я получаю этот камень от компилятора: метод' call (capture # 4-of? Extends B ) в типе Callback , захват # 5-of? Extends C > не применим для аргументов (B ' – Jocken

Смежные вопросы