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 extends Rankable> 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.