Construtores genéricos em Java

Construtores genéricos em Java

1. Visão geral

Discutimos anteriormente os fundamentos deJava Generics. Neste tutorial, daremos uma olhada em Construtores genéricos em Java.

Um construtor genérico é um construtor que possui pelo menos um parâmetro de um tipo genérico.

Veremos que os construtores genéricos não precisam estar em uma classe genérica, e nem todos os construtores em uma classe genérica precisam ser genéricos.

2. Classe não genérica

Primeiro, temos uma classe simplesEntry, que não é uma classe genérica:

public class Entry {
    private String data;
    private int rank;
}

Nesta aula, vamos adicionar dois construtores: um construtor básico com dois parâmetros e um construtor genérico.

2.1. Construtor básico

O primeiro construtorEntry é um construtor simples com dois parâmetros:

public Entry(String data, int rank) {
    this.data = data;
    this.rank = rank;
}

Agora, vamos usar este construtor básico para criar um objetoEntry:

@Test
public void givenNonGenericConstructor_whenCreateNonGenericEntry_thenOK() {
    Entry entry = new Entry("sample", 1);

    assertEquals("sample", entry.getData());
    assertEquals(1, entry.getRank());
}

2.2. Construtor genérico

Em seguida, nosso segundo construtor é um construtor genérico:

public  Entry(E element) {
    this.data = element.toString();
    this.rank = element.getRank();
}

Embora a classeEntry não seja genérica, ela possui um construtor genérico, pois possui um parâmetroelement do tipoE.

O tipo genéricoE é limitado e deve implementar as interfacesRankableeSerializable.

Agora, vamos dar uma olhada na interfaceRankable, que tem um método:

public interface Rankable {
    public int getRank();
}

E, suponha que temos uma classeProduct que implementa a interfaceRankable:

public class Product implements Rankable, Serializable {
    private String name;
    private double price;
    private int sales;

    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public int getRank() {
        return sales;
    }
}

Podemos então usar o construtor genérico para criar objetosEntry usando umProduct:

@Test
public void givenGenericConstructor_whenCreateNonGenericEntry_thenOK() {
    Product product = new Product("milk", 2.5);
    product.setSales(30);

    Entry entry = new Entry(product);

    assertEquals(product.toString(), entry.getData());
    assertEquals(30, entry.getRank());
}

3. Classe genérica

A seguir, daremos uma olhada em uma classe genérica chamadaGenericEntry:

public class GenericEntry {
    private T data;
    private int rank;
}

Vamos adicionar os mesmos dois tipos de construtores da seção anterior nesta classe também.

3.1. Construtor básico

Primeiro, vamos escrever um construtor simples e não genérico para nossa classeGenericEntry:

public GenericEntry(int rank) {
    this.rank = rank;
}

Mesmo queGenericEntry seja uma classe genérica, este é um construtor simples que não possui um parâmetro de um tipo genérico.

Agora, podemos usar este construtor para criar umGenericEntry<String>:

@Test
public void givenNonGenericConstructor_whenCreateGenericEntry_thenOK() {
    GenericEntry entry = new GenericEntry(1);

    assertNull(entry.getData());
    assertEquals(1, entry.getRank());
}

3.2. Construtor genérico

A seguir, vamos adicionar o segundo construtor à nossa classe:

public GenericEntry(T data, int rank) {
    this.data = data;
    this.rank = rank;
}

This is a generic constructor, as it has a data parameter of the generic type T. Observe que não precisamos adicionar<T> na declaração do construtor, pois está implicitamente lá.

Agora, vamos testar nosso construtor genérico:

@Test
public void givenGenericConstructor_whenCreateGenericEntry_thenOK() {
    GenericEntry entry = new GenericEntry("sample", 1);

    assertEquals("sample", entry.getData());
    assertEquals(1, entry.getRank());
}

4. Construtor genérico com tipo diferente

Em nossa classe genérica, também podemos ter um construtor com um tipo genérico que é diferente do tipo genérico da classe:

public  GenericEntry(E element) {
    this.data = (T) element;
    this.rank = element.getRank();
}

Este construtorGenericEntry tem um parâmetroelement com tipoE, que é diferente do tipoT. Vamos ver em ação:

@Test
public void givenGenericConstructorWithDifferentType_whenCreateGenericEntry_thenOK() {
    Product product = new Product("milk", 2.5);
    product.setSales(30);

    GenericEntry entry = new GenericEntry(product);

    assertEquals(product, entry.getData());
    assertEquals(30, entry.getRank());
}

Observe que:

  • Em nosso exemplo, usamosProduct (E) para criar umGenericEntry do tipoSerializable (T)

  • Só podemos usar este construtor quando o parâmetro do tipoE pode ser convertido paraT

5. Vários tipos genéricos

A seguir, temos a classe genéricaMapEntry com dois tipos genéricos:

public class MapEntry {
    private K key;
    private V value;

    public MapEntry(K key, V value) {
        this.key = key;
        this.value = value;
    }
}

MapEntry has one generic constructor with two parameters, each of a different type. Vamos usá-lo em um teste de unidade simples:

@Test
public void givenGenericConstructor_whenCreateGenericEntryWithTwoTypes_thenOK() {
    MapEntry entry = new MapEntry("sample", 1);

    assertEquals("sample", entry.getKey());
    assertEquals(1, entry.getValue().intValue());
}

6. Curingas

Finalmente, podemos usar curingas em um construtor genérico:

public GenericEntry(Optional optional) {
    if (optional.isPresent()) {
        this.data = (T) optional.get();
        this.rank = optional.get().getRank();
    }
}

Aqui, usamos curingas neste construtorGenericEntry para limitar o tipoOptional:

@Test
public void givenGenericConstructorWithWildCard_whenCreateGenericEntry_thenOK() {
    Product product = new Product("milk", 2.5);
    product.setSales(30);
    Optional optional = Optional.of(product);

    GenericEntry entry = new GenericEntry(optional);

    assertEquals(product, entry.getData());
    assertEquals(30, entry.getRank());
}

Observe que devemos ser capazes de lançar o tipo de parâmetro opcional (em nosso caso,Product) para o tipoGenericEntry (em nosso caso,Serializable).

7. Conclusão

Neste artigo, aprendemos como definir e usar construtores genéricos em classes genéricas e não genéricas.

O código-fonte completo pode ser encontrado emGitHub.