Os chamados genéricos: Permite especificar parâmetros de tipo ao definir classes e interfaces. Este parâmetro de tipo será determinado ao declarar variáveis e criar objetos (ou seja, passar parâmetros de tipo reais, que também podem ser chamados de argumentos de tipo).
Classe ou interface genérica
O código de cópia da sintaxe "diamante" é o seguinte:
//definição
interface pública List<E> estende Collection<E>
classe pública HashMap<K,V> estende AbstractMap<K,V> implementa Map<K,V>, Cloneable, Serializable
//usar
Lista<String> lista = new ArrayList();
//Depois do Java 7, você pode omitir o parâmetro de tipo dos colchetes angulares atrás dele.
Lista<String> lista = new ArrayList<>();
Derive uma subclasse de uma classe genérica
Copie o código do código da seguinte forma:
//Método 1
App de classe pública estende GenericType<String>
//Método 2
classe pública App<T> estende GenericType<T>
//Método 3
App de classe pública estende GenericType
Pseudo genéricos
Não existe uma classe genérica real. As classes genéricas são transparentes para a máquina virtual Java. Portanto, os parâmetros de tipo não são permitidos. em métodos estáticos, blocos de inicialização estáticos e variáveis estáticas.
- Os métodos a seguir estão todos errados. O código de cópia é o seguinte:
dados T estáticos privados;
estático{
Tf;
}
função estática pública void(){
Nome T = 1;
}
O exemplo a seguir pode verificar se não há classe genérica. Copie o código:
public static void main(String[] args){
Lista<String> a1 = new ArrayList<>();
Lista<Integer> a2 = new ArrayList<>();
System.out.println(a1.getClass() == a2.getClass());
System.out.println(a1.getClass());
System.out.println(a2.getClass());
}
O código de cópia de saída é o seguinte:
verdadeiro
classe java.util.ArrayList
classe java.util.ArrayList
Digite curinga
Em primeiro lugar, deve ficar claro que se Foo é a classe pai de Bar, mas List<Foo> não é a classe pai de List<Bar> Para representar várias classes pai genéricas, Java usa "?" curingas genéricos. Ou seja, List<?> representa a classe pai de várias listas genéricas. Com esse tipo de curinga, os genéricos de lista não podem definir (definir) elementos, mas podem apenas obter (obter) elementos. Como o programa não pode determinar os tipos na Lista, ele não pode adicionar objetos. Mas o objeto obtido deve ser do tipo Object.
Os seguintes métodos serão compilados com erros:
Copie o código do código da seguinte forma:
Lista<?> lista = new ArrayList<>();
lista.add(novo Objeto());
Algumas ideias:
1. Os objetos List<String> não podem ser usados como objetos List<Object>, ou seja: a classe List<String> não é uma subclasse da classe List<Object>.
2. Arrays são diferentes dos genéricos: assumindo que Foo é um subtipo (subclasse ou subinterface) de Bar, então Foo[] ainda é um subtipo de Bar[] mas G<Foo> não é G<Bar> subtipo.
3. Para representar a classe pai de várias listas genéricas, precisamos usar curingas de tipo. O curinga de tipo é um ponto de interrogação (?). > (significando lista desconhecida de elementos de tipo). Este ponto de interrogação (?) é chamado de caractere curinga e seu tipo de elemento pode corresponder a qualquer tipo.
Limite superior do curinga
List<? extends SuperType> representa a classe pai ou ela mesma de todas as listas genéricas de SuperType. Genéricos com limites superiores curinga não podem ter métodos set, apenas métodos get.
Definir o limite superior de curingas pode resolver os seguintes problemas: Dog é uma subclasse de Animal e existe um método getSize para obter o número de listas recebidas.
classe abstrata Animal {
público abstrato void run();
}
classe Cachorro estende Animal {
execução void pública() {
System.out.println("Cão correndo");
}
}
aplicativo de classe pública {
public static void getSize(List<Animal> lista) {
System.out.println(list.size());
}
public static void main(String[] args) {
Lista<Cão> lista = new ArrayList<>();
getSize(list); // Erro de compilação aqui
}
}
A razão para o erro de programação aqui é que List<Animal> não é a classe pai de List<Dog>. A primeira solução é alterar o parâmetro formal List<Animal> no método getSize para List<?>, mas neste caso, a conversão forçada do tipo é necessária toda vez que o objeto é obtido, o que é mais problemático. Usar o limite superior do curinga resolve esse problema muito bem. Você pode alterar List<Animal> para List<estende Animal> e não haverá erros na compilação e nenhuma conversão de tipo será necessária.
Limite inferior para curingas
List<? super SubType> representa o limite inferior da lista genérica do subtipo. Genéricos com limites superiores curinga não podem ter métodos get, apenas métodos set.
Métodos genéricos
Se você definir classes e interfaces sem usar parâmetros de tipo, mas quiser definir você mesmo os parâmetros de tipo ao definir métodos, isso também será possível. A assinatura do método de um método genérico tem mais declarações de parâmetros de tipo do que a assinatura de método de um método comum. As declarações de parâmetros de tipo são colocadas entre colchetes angulares. modificadores e tipos de valor de retorno do método O formato da sintaxe é o seguinte:
Copie o código do código da seguinte forma:
Modificador de valor de retorno tipo nome do método (lista de tipos) {
//corpo do método
}
Os métodos genéricos permitem que parâmetros de tipo sejam usados para expressar dependências de tipo entre um ou mais parâmetros de um método ou entre valores e parâmetros de retorno do método. Se não houver tal dependência de tipo, métodos genéricos não deverão ser usados. O método copy de Collections usa métodos genéricos:
Copie o código do código da seguinte forma:
public static <T> void copy(List<? super T> dest, List<? extends T> src){ ...}
Este método requer que o tipo src seja uma subclasse do tipo dest ou ele mesmo.
Apagar e converter
No código genérico estrito, as classes com declarações genéricas devem sempre ter parâmetros de tipo. Porém, para ser consistente com o código Java antigo, também é permitido usar classes com declarações genéricas sem especificar parâmetros de tipo. Se nenhum parâmetro de tipo for especificado para esta classe genérica, o parâmetro de tipo será chamado de tipo bruto e o padrão será o primeiro tipo de limite superior especificado quando o parâmetro foi declarado.
Quando um objeto com informações genéricas é atribuído a outra variável sem informações genéricas, todas as informações de tipo entre colchetes angulares são descartadas. Por exemplo, se um tipo List<String> for convertido em List, a verificação de tipo dos elementos da coleção da List se tornará o limite superior da variável de tipo (ou seja, Object). Esta situação é chamada de apagamento.
O código de cópia de exemplo é o seguinte:
classe Apple<T estende número>
{
Tamanho T;
publicApple()
{
}
Apple pública (tamanho T)
{
este.tamanho = tamanho;
}
conjunto vazio públicoSize (tamanho T)
{
este.tamanho = tamanho;
}
público T getSize()
{
retorne este.tamanho;
}
}
classe pública ErasureTest
{
público estático void principal(String[] args)
{
Apple<Integer> a = new Apple<>(6);
// O método getSize de a retorna um objeto Integer
Inteiro como = a.getSize();
// Atribui o objeto a à variável Apple, perdendo as informações de tipo entre colchetes angulares
Maçã b = uma; // ②
//b só sabe que o tipo de tamanho é Number
Tamanho do número1 = b.getSize();
//O código a seguir causa um erro de compilação
Tamanho inteiro2 = b.getSize();
}
}