Caractere curinga de genéricos Java com várias classes

385

Quero ter um objeto Class, mas quero forçar qualquer classe que represente a estender a classe A e implementar a interface B.

Eu posso fazer:

Class<? extends ClassA>

Ou:

Class<? extends InterfaceB>

mas eu não posso fazer as duas coisas. Existe uma maneira de fazer isso?

Alex Beardsley
fonte

Respostas:

613

Na verdade, você pode fazer o que quiser. Se você deseja fornecer várias interfaces ou uma classe mais interfaces, você deve ter seu curinga parecido com este:

<T extends ClassA & InterfaceB>

Consulte o Tutorial sobre genéricos em sun.com, especificamente a seção Parâmetros de tipo limitado , na parte inferior da página. Você pode realmente listar mais de uma interface, se desejar, usando & InterfaceNamepara cada uma que precisar.

Isso pode ser arbitrariamente complicado. Para demonstrar, consulte a declaração JavaDoc de Collections#max, que (agrupada em duas linhas) é:

public static <T extends Object & Comparable<? super T>> T
                                           max(Collection<? extends T> coll)

por que tão complicado? Como dito nas Perguntas frequentes sobre Java Generics: Para preservar a compatibilidade binária .

Parece que isso não funciona para declaração de variáveis, mas funciona ao colocar um limite genérico em uma classe. Assim, para fazer o que quiser, você pode ter que pular algumas voltas. Mas você consegue fazer isso. Você pode fazer algo assim, colocando um limite genérico em sua classe e depois:

class classB { }
interface interfaceC { }

public class MyClass<T extends classB & interfaceC> {
    Class<T> variable;
}

para obter variableisso tem a restrição que você deseja. Para obter mais informações e exemplos, consulte a página 3 de Genéricos em Java 5.0 . Observe que, em <T extends B & C>, o nome da classe deve vir primeiro e as interfaces seguem. E, claro, você pode listar apenas uma única classe.

Eddie
fonte
94
Isso é útil. Vale ressaltar que a classe deve vir primeiro, você não pode dizer '<T estende InterfaceB & ClassA>'.
ERIC
5
Como você faz o mesmo com T, deve estender uma classe OU implementar uma interface?
Ragunath Jawahar
11
@RagunathJawahar: Você não pode ficar muito aberto sobre isso. Você precisa ter alguns limites, e um deles é saber com antecedência onde você terá interfaces e onde terá classes e como usará a herança. Você praticamente precisa saber para um parâmetro de tipo se será uma classe ou uma interface.
Eddie #
4
A primeira linha da sua resposta diz "faça com que seu curinga seja mais ou menos assim". Não existe ?nessa expressão, então é realmente um "curinga"? Eu pergunto porque não consigo que o conceito "estendendo duas coisas ao mesmo tempo" funcione quando o parâmetro type é realmente um curinga.
The111
11
Sim, não é realmente um curinga e você não pode fazer o que o OP pediu com um curinga. Você pode fazer o que o OP queria, efetivamente, apenas não com um curinga.
Eddie
17

Você não pode fazê-lo com parâmetros do tipo "anônimo" (ou seja, curingas que usam ?), mas pode fazê-lo com parâmetros do tipo "nomeado". Simplesmente declare o parâmetro type no método ou no nível da classe.

import java.util.List;
interface A{}
interface B{}
public class Test<E extends B & A, T extends List<E>> {
    T t;
}
user2861738
fonte
2
Por que os curingas não são permitidos? ou seja, não vejo por que isso não deve ser válido: ArrayList <? estende a lista ClassA & InterfaceB>; E então se você puxou um elemento fora da lista, você pode atribuí-lo a uma variável do tipo ClassA ou do tipo InterfaceB
Mark
Substituir o curinga por uma variável é uma ótima técnica! Mas nem sempre funciona. Por exemplo, Java não permite variáveis ​​de tipo de nomes em tipos de valor de anotação, enquanto permite caracteres curinga.
sigpwned