2015-12-16 7 views
-1

Мне нужно создать экземпляр объекта с неизвестным общим типом, а затем применить к нему общий метод. Вот где я стою:Создавать и применять общий метод с неизвестным общим типом - Java

public static void main(String[] args) 
{ 
    BarIf bar = newRandomBarImpl(); 

    Foo foo1 = newFooBar(bar.getClass()); // warning 
    Foo<?> foo2 = newFooBar(bar.getClass()); // error 
    Foo<? extends BarIf> foo3 = newFooBar(bar.getClass()); // error 

    foo1.doSomething(bar); // warning 
    foo2.doSomething(bar); // error 
    foo3.doSomething(bar); // error 
} 

static <T extends FooIf<S>, S extends BarIf> T newFooBar(Class<S> barClass){} 

static <T extends BarIf> T newRandomBarImpl(){} 

interface FooIf<T extends BarIf> 
{ 
    public void doSomething(T t); 
} 

interface BarIf{} 

class Foo<T extends BarIf> implements FooIf<T> 
{ 
    public void doSomething(T t){} 
} 

Самое странное в том, что для foo2 и foo3, метод newFooBar() возвращает FooIf, а не Foo. Я предполагаю, что вывод типа грязный. Но я не могу передать общие параметры метода, так как я не знаю тип Bar.

Что мне нужно, это Foo<bar.getClass()>. Есть ли способ сделать это?

Я пробовал использовать TypeToken, но в итоге я получил тип T, а не фактический тип бара. Есть ли шанс использовать это?

+0

Почему вы не объявляете переменные как 'FooIf ' вместо 'Foo '? – Blip

+0

Потому что я хочу применить методы, которые не могут быть объявлены в FooIf, но только в Foo. Вот почему я использую общие методы в первую очередь. – Bastien

+0

Почему вы не использовали типу? – Blip

ответ

1

Прежде всего, заявление, как

static <T extends BarIf> T newRandomBarImpl(){} 

нонсенс. Он в основном говорит «независимо от того, что вызывающий заменяет T, метод вернет его». Иными словами, вы можете написать

ArbitraryTypeExtendingBarIf x=newRandomBarImpl(); 

без предупреждения о компиляторе. Очевидно, что это не сработает. newRandomBarImpl() ничего не знает о себе ArbitraryTypeExtendingBarIf. Имя метода предполагает, что вы на самом деле хотите, чтобы выразить, что newRandomBarImpl() может вернуть произвольное выполнение BarIf, но это ненужное использование генериков,

BarIf newRandomBarImpl(){} 

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

То же самое относится и к декларации

static <T extends FooIf<S>, S extends BarIf> T newFooBar(Class<S> barClass){} 

Он также утверждает, что абонент может выбрать, какую реализацию FooIf метод вернет. Правильное заявление будет

static <S extends BarIf> FooIf<S> newFooBar(Class<S> barClass){} 

как метод решает, реализация FooIf он вернется, не вызывающий.


Что касается других ваших попыток решить FooIf, вы не можете работать таким образом, используя тип параметризованный с символом, не может это исправить с помощью отражения. Но вы можете написать общий код, используя параметр типа:

public static void main(String[] args) 
{ 
    BarIf bar = newRandomBarImpl(); 
    performTheAction(bar.getClass(), bar); 
} 
static <T extends BarIf> void performTheAction(Class<T> cl, BarIf obj) { 
    FooIf<T> foo=newFooBar(cl); 
    foo.doSomething(cl.cast(obj)); 
} 
static <S extends BarIf> FooIf<S> newFooBar(Class<S> barClass){} 
static BarIf newRandomBarImpl(){} 

interface FooIf<T extends BarIf> { 
    public void doSomething(T t); 
} 
interface BarIf{} 

Метод performTheAction является универсальным, другими словами, работает с неизвестным типом, выраженным в качестве параметра типа T. Этот метод может быть вызван неизвестным типом ? extends BarIf, как показано в методе main.

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

Вы можете просто использовать базовый класс BarIf здесь, независимо от фактического подтипа BarIf объект имеет:

BarIf bar = newRandomBarImpl(); 
FooIf<BarIf> foo=newFooBar(BarIf.class); 
foo.doSomething(bar); 

Обратите внимание, что если вы хотите использовать методы фактического типа реализации Foo, не указанные в интерфейс, вам нужно будет нарисовать FooIf до Foo. Вы можете отличить FooIf<BarIf> до Foo<BarIf> без предупреждения, поскольку преобразование типового типа верное, если Foo<X> implements FooIf<X>.

Однако он может не работать во время выполнения, поскольку метод newFooBar не требуется возвращать экземпляр Foo, а не любую другую реализацию FooIf. Вот почему явный тип приведения является единственным правильным решением, поскольку он документирует, что есть предположение о фактическом типе времени выполнения объекта. Все остальные попытки будут генерировать по крайней мере одно предупреждение компилятора где-нибудь.

+0

Мне удалось заставить его работать. Спасибо. Наверное, у меня были свои дженерики. – Bastien

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