Java Basics (1) — Uso simples de genéricos

1. Definição e funções dos genéricos

Genéricos são um mecanismo de programação que permite o uso de tipos parametrizados ao escrever código para obter segurança de tipo em tempo de compilação. A seguir está o papel dos genéricos:

  1. Melhore a legibilidade e a capacidade de manutenção do código : ao usar parâmetros genéricos em seu código, você pode torná-lo mais claro, legível e fácil de manter.

  2. Segurança de código aprimorada : os genéricos podem verificar tipos em tempo de compilação, evitando erros de conversão de tipo em tempo de execução.

  3. Maior capacidade de reutilização de código : os genéricos permitem escrever código comum em diferentes tipos de dados, aumentando assim a capacidade de reutilização do código.

  4. Simplifique o código : o uso de genéricos pode simplificar o código, evitando escrever códigos semelhantes repetidamente.

Resumindo, os genéricos são um mecanismo de programação poderoso que pode ajudar os desenvolvedores a escrever códigos mais legíveis, fáceis de manter, seguros e reutilizáveis.

Os genéricos Java executam o apagamento de tipo (Type Erasure) durante a compilação. Apagamento de tipo significa que quando o compilador compila código genérico, ele apagará o tipo genérico para seu tipo original ou tipo qualificado, de modo que o tipo genérico não exista em tempo de execução. Ao executar o apagamento de tipo, o compilador substitui os parâmetros de tipo genérico por seu tipo delimitador (qualificado) (se houver) ou pelo tipo Object se nenhum tipo qualificado for especificado.

Por exemplo, uma classe genérica é definida da seguinte forma:

public class Box<T> {
    
    
    private T content;
    public void setContent(T content) {
    
    
        this.content = content;
    }
    public T getContent() {
    
    
        return content;
    }
}

Após a compilação, o apagamento de tipo apagará o tipo genérico T para o tipo Objeto, e o bytecode gerado é o seguinte:

public class Box {
    
    
    private Object content;
    public void setContent(Object content) {
    
    
        this.content = content;
    }
    public Object getContent() {
    
    
        return content;
    }
}

Deve-se notar que embora o tipo genérico não exista em tempo de execução, o compilador ainda executa a verificação de tipo no tipo genérico durante a compilação para garantir a segurança do tipo.

2. Uso de genéricos

1. Os genéricos são definidos na classe

código mostrado abaixo:

class CommonUtil {
    
    

  public Object obj;

  public Object getObj() {
    
    
    return obj;
  }

  public void setObj(Object obj) {
    
    
    this.obj = obj;
  }
}

Ao usá-lo, o código é o seguinte:

  public static void main(String[] args) {
    
    

    CommonUtil tool = new CommonUtil<>();
    
    tool.setObj(2);
    Integer val1 = (Integer)tool.getObj();
    
	tool.setObj("hello java");
    String val2 = (String)tool.getObj();
  }

Como pode ser visto no código acima, a conversão forçada é necessária sempre que o valor é obtido. Erros de conversão forçada podem ocorrer facilmente se você não tomar cuidado. Então, existe uma maneira de evitar a coerção de tipo? A resposta é: genéricos. Ao definir genéricos em uma classe, você pode evitar a conversão de tipo. O código melhorado é o seguinte:

class CommonUtil<T> {
    
    

  public T obj;

  public T getObj() {
    
    
    return obj;
  }

  public void setObj(T obj) {
    
    
    this.obj = obj;
  }
}

Ao criar uma instância, passe o tipo específico. O código é o seguinte:

  public static void main(String[] args) {
    
    

    CommonUtil<Integer> tool = new CommonUtil<>();
    tool.setObj(2);
    Integer val1 = tool.getObj();
    
    CommonUtil<String> tool = new CommonUtil<>();
	tool.setObj("hello java");
    String val2 = tool.getObj();
  }

2. Método de definição genérica

Há um ponto não tão bom sobre a definição genérica da classe. Dê uma olhada no exemplo a seguir. O código é o seguinte:

class CommonUtil<T> {
    
    

  public void show(T obj){
    
    
    System.out.println("obj = " + obj);
  }
}

O código de uso do código é o seguinte:

    CommonUtil<Integer> tool1 = new CommonUtil<>();
    tool1.show(value);

    CommonUtil<String> tool2 = new CommonUtil<>();
    tool2.show("111");
    
	CommonUtil<Person> tool2 = new CommonUtil<>();
    tool2.show(new Person());

Você descobriu que toda vez que você chama o método show(), você precisa criar uma instância, porque você pode especificar o tipo de parâmetro específico ao criar. Mas criar objetos dessa maneira é muito problemático. Como resolver isso? Você pode definir genéricos nos métodos. O código é o seguinte:

class CommonUtil {
    
    

  public <W> void show(W obj){
    
    
    System.out.println("obj = " + obj);
  }
}

O código de uso é o seguinte:

    CommonUtil tool1 = new CommonUtil<>();
    tool1.show(value);
    tool1.show("111");
    tool1.show(new Person());

Finalmente, o tipo de parâmetro específico é passado quando o método é chamado, para que a criação repetida de objetos possa ser evitada e um método possa ser chamado repetidamente, o que é muito semelhante ao Object. Métodos estáticos , incluindo modificação estática , também podem ser usados. código mostrado abaixo:

class CommonUtil {
    
    

  public static <W> void show(W obj){
    
    
    System.out.println("obj = " + obj);
  }
}

O código de uso é o seguinte:

    CommonUtil.show(value);
    CommonUtil.show("111");
    CommonUtil.show(new Person());

No entanto, ao definir genéricos em métodos estáticos, observe que este método estático não pode usar genéricos na classe. Por que? Como o tipo de parâmetro específico é especificado quando a classe é criada e o método estático é carregado na JVM antes da classe ser instanciada, não há como saber qual tipo de parâmetro específico sua classe passou ao criar uma instância. Exemplos de erros são os seguintes:

class CommonUtil<W> {
    
    

  public static <W> void show(W obj){
    
    
    System.out.println("obj = " + obj);
  }
}

3. Os genéricos são definidos na interface

Às vezes, os genéricos são definidos em interfaces e o código é o seguinte:

public interface Inter<T> {
    
    

	public void print(T obj);
}

Os genéricos na interface podem ser especificados na subclasse ou não. O tipo específico especificado pela subclasse é o seguinte:

public InterImpl implements Inter<String> {
    
    
	// 接口上的方法
	public void print(String obj){
    
    }
	// 子类独有方法
	protect void show(Object obj){
    
    }
}

Quando em uso, o código é o seguinte:

	Inter<String> inter = new InterImpl();
	inter.print("hello");
	
	InterImpl impl = new InterImpl();
	impl.show(new Object());

Ou defina genéricos em subclasses e interfaces para definir tipos específicos. O código é o seguinte:

public InterImpl<W> implements Inter<String> {
    
    
	// 接口上的方法
	public void print(String obj){
    
    }
	// 子类独有方法
	protect void show(W obj){
    
    }
}

Quando em uso, o código é o seguinte:

	Inter<String> inter = new InterImpl();
	inter.print("hello");
	
	InterImpl<Integer> impl = new InterImpl();
	impl.show(new Integer(10));

Se o tipo específico não for conhecido na subclasse, você também pode definir um genérico e passar a subclasse genérica para a interface. O código é o seguinte:

public InterImpl<W> implements Inter<W> {
    
    
	// 接口上的方法
	public void print(W obj){
    
    }
	// 子类独有方法
	protect void show(W obj){
    
    }
}

Quando em uso, o código é o seguinte:

	Inter<String> inter = new InterImpl();
	inter.print("hello");
	
	InterImpl<Integer> impl = new InterImpl();
	impl.show(new Integer(10));

4. Curingas genéricos

Por exemplo, o código é o seguinte:

public class FanxinDemo {
    
    

  public static void print(Collection<String> list) {
    
    
    list.forEach(e->{
    
    
      System.out.println("e = " + e);
    });
  }

  public static void main(String[] args) {
    
    

    List<String> list1 = new ArrayList<>();
    list1.add("a");
    list1.add("b");
    list1.add("c");

    List<Integer> list2 = new ArrayList<>();
    list2.add(1);
    list2.add(2);

    print(list1);
 }
}

Pode-se descobrir que o método print() só pode gerar tipos de parâmetros de String, mas não outros parâmetros. Como esse código pode ser compartilhado? Aqui apresentamos um curinga genérico ?. Quando você não sabe que tipo é um genérico, você pode usar esta representação temporária. O código é o seguinte:

public class FanxinDemo {
    
    

  public static void print(Collection<?> list) {
    
    
    list.forEach(e->{
    
    
      System.out.println("e = " + e);
    });
  }

  public static void main(String[] args) {
    
    

    List<String> list1 = new ArrayList<>();
    list1.add("a");
    list1.add("b");
    list1.add("c");

    List<Integer> list2 = new ArrayList<>();
    list2.add(1);
    list2.add(2);

    print(list1);
    print(list2);
 }
}

Mas suponha que você não queira que o método print() seja usado por parâmetros do tipo Integer, o que você deve fazer? Isto requer qualificação genérica.

5. Limitação genérica

A qualificação genérica pode limitar o intervalo do tipo de parâmetro. ?Esse intervalo de nível é muito grande e inseguro. O intervalo é limitado pelas palavras-chave extends e super. No entanto, essas duas palavras-chave só podem ser de herança única, portanto, também são limitadas. Existem dois tipos de restrições: disponibilizá-lo para todas as classes pai e disponibilizá-lo para todas as subclasses.

5.1. Disponibilize-o para todas as classes principais

código mostrado abaixo:

  public static void print(Collection<? super Integer> list) {
    
    
    list.forEach(e->{
    
    
      System.out.println("e = " + e);
    });
  }

Indica que todas as classes pai de Integer podem usar o método print().

5.2. Disponibilize-o para todas as subclasses

código mostrado abaixo:

  public static void print(Collection<? extends String>> list) {
    
    
    list.forEach(e->{
    
    
      System.out.println("e = " + e);
    });
  }

Indica que todas as subclasses String podem usar o método print().

Acho que você gosta

Origin blog.csdn.net/qq_35971258/article/details/129031946
Recomendado
Clasificación