1. Overview
1.概述
We previously discussed the basics of Java Generics. In this tutorial, we’ll have a look at Generic Constructors in Java.
我们之前讨论了Java泛型的基本知识。在本教程中,我们将了解一下Java中的泛型构造器。
A generic constructor is a constructor that has at least one parameter of a generic type.
通用构造函数是指至少有一个通用类型的参数的构造函数。
We’ll see that generic constructors don’t have to be in a generic class, and not all constructors in a generic class have to be generic.
我们将看到,泛型构造函数不一定要在泛型类中出现,而泛型类中的构造函数也不一定都是泛型。
2. Non-Generic Class
2.非通用类
First, we have a simple class Entry, which is not a generic class:
首先,我们有一个简单的类Entry,它不是一个通用类。
public class Entry {
private String data;
private int rank;
}
In this class, we’ll add two constructors: a basic constructor with two parameters, and a generic constructor.
在这个类中,我们将添加两个构造函数:一个带有两个参数的基本构造函数,以及一个泛型构造函数。
2.1. Basic Constructor
2.1.基本构造函数
The first Entry constructor is a simple constructor with two parameters:
第一个Entry构造函数是一个有两个参数的简单构造函数。
public Entry(String data, int rank) {
this.data = data;
this.rank = rank;
}
Now, let’s use this basic constructor to create an Entry object:
现在,让我们使用这个基本构造函数来创建一个Entry对象。
@Test
public void givenNonGenericConstructor_whenCreateNonGenericEntry_thenOK() {
Entry entry = new Entry("sample", 1);
assertEquals("sample", entry.getData());
assertEquals(1, entry.getRank());
}
2.2. Generic Constructor
2.2.通用构造函数
Next, our second constructor is a generic constructor:
接下来,我们的第二个构造函数是一个通用构造函数。
public <E extends Rankable & Serializable> Entry(E element) {
this.data = element.toString();
this.rank = element.getRank();
}
Although the Entry class isn’t generic, it has a generic constructor, as it has a parameter element of type E.
尽管Entry类不是通用的,但它有一个通用的构造函数,因为它有一个E类型的参数element。
The generic type E is bounded and should implement both Rankable and Serializable interfaces.
通用类型E是有边界的,应该实现Rankable和Serializable接口。
Now, let’s have a look at the Rankable interface, which has one method:
现在,让我们看一下Rankable接口,它有一个方法。
public interface Rankable {
public int getRank();
}
And, suppose we have a class Product that implements the Rankable interface:
而且,假设我们有一个实现了Rankable接口的Product类。
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;
}
}
We can then use the generic constructor to create Entry objects using a Product:
然后,我们可以使用通用构造函数,使用Product创建Entry对象。
@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. Generic Class
3.通用类
Next, we’ll have a look at a generic class called GenericEntry:
接下来,我们来看看一个叫做GenericEntry的通用类。
public class GenericEntry<T> {
private T data;
private int rank;
}
We’ll add the same two types of constructors as the previous section in this class as well.
我们也将在这个类中添加与上一节相同的两种类型的构造函数。
3.1. Basic Constructor
3.1.基本构造函数
First, let’s write a simple, non-generic constructor for our GenericEntry class:
首先,让我们为我们的GenericEntry类写一个简单的、非通用的构造函数。
public GenericEntry(int rank) {
this.rank = rank;
}
Even though GenericEntry is a generic class, this is a simple constructor that doesn’t have a parameter of a generic type.
尽管GenericEntry是一个泛型类,但这是一个简单的构造函数,没有一个泛型的参数。
Now, we can use this constructor to create a GenericEntry<String>:
现在,我们可以使用这个构造函数来创建一个GenericEntry<String>。
@Test
public void givenNonGenericConstructor_whenCreateGenericEntry_thenOK() {
GenericEntry<String> entry = new GenericEntry<String>(1);
assertNull(entry.getData());
assertEquals(1, entry.getRank());
}
3.2. Generic Constructor
3.2.通用构造函数
Next, let’s add the second constructor to our class:
接下来,让我们为我们的类添加第二个构造函数。
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. Note that we don’t need to add <T> in the constructor declaration, as it’s implicitly there.
这是一个泛型构造函数,因为它有一个泛型data的参数T。注意,我们不需要在构造函数声明中添加<T>,因为它已经隐含在那里。
Now, let’s test our generic constructor:
现在,让我们测试一下我们的通用构造函数。
@Test
public void givenGenericConstructor_whenCreateGenericEntry_thenOK() {
GenericEntry<String> entry = new GenericEntry<String>("sample", 1);
assertEquals("sample", entry.getData());
assertEquals(1, entry.getRank());
}
4. Generic Constructor with Different Type
4.具有不同类型的通用构造函数
In our generic class, we can also have a constructor with a generic type that’s different from the class’ generic type:
在我们的泛型类中,我们也可以有一个构造函数,其泛型类型与该类的泛型类型不同。
public <E extends Rankable & Serializable> GenericEntry(E element) {
this.data = (T) element;
this.rank = element.getRank();
}
This GenericEntry constructor has a parameter element with type E, which is different from the T type. Let’s see it in action:
这个GenericEntry构造函数有一个参数element,其类型E,与T类型不同。让我们看看它的作用。
@Test
public void givenGenericConstructorWithDifferentType_whenCreateGenericEntry_thenOK() {
Product product = new Product("milk", 2.5);
product.setSales(30);
GenericEntry<Serializable> entry = new GenericEntry<Serializable>(product);
assertEquals(product, entry.getData());
assertEquals(30, entry.getRank());
}
Note that:
请注意,。
- In our example, we used Product (E) to create a GenericEntry of type Serializable (T)
- We can only use this constructor when the parameter of type E can be cast to T
5. Multiple Generic Types
5.多种通用类型
Next, we have the generic class MapEntry with two generic types:
接下来,我们有一个通用类MapEntry,有两个通用类型。
public class MapEntry<K, V> {
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. Let’s use it in a simple unit test:
MapEntry有一个通用构造函数,有两个参数,每个都是不同的类型。让我们在一个简单的单元测试中使用它。
@Test
public void givenGenericConstructor_whenCreateGenericEntryWithTwoTypes_thenOK() {
MapEntry<String,Integer> entry = new MapEntry<String,Integer>("sample", 1);
assertEquals("sample", entry.getKey());
assertEquals(1, entry.getValue().intValue());
}
6. Wildcards
6.野生动物
Finally, we can use wildcards in a generic constructor:
最后,我们可以在通用构造函数中使用通配符。
public GenericEntry(Optional<? extends Rankable> optional) {
if (optional.isPresent()) {
this.data = (T) optional.get();
this.rank = optional.get().getRank();
}
}
Here, we used wildcards in this GenericEntry constructor to bound the Optional type:
这里,我们在这个GenericEntry构造函数中使用通配符来绑定Optional类型。
@Test
public void givenGenericConstructorWithWildCard_whenCreateGenericEntry_thenOK() {
Product product = new Product("milk", 2.5);
product.setSales(30);
Optional<Product> optional = Optional.of(product);
GenericEntry<Serializable> entry = new GenericEntry<Serializable>(optional);
assertEquals(product, entry.getData());
assertEquals(30, entry.getRank());
}
Note that we should be able to cast the optional parameter type (in our case, Product) to the GenericEntry type (in our case, Serializable).
请注意,我们应该能够将可选的参数类型(在我们的例子中是Product)投给GenericEntry类型(在我们的例子中是Serializable)。
7. Conclusion
7.结语
In this article, we learned how to define and use generic constructors in both generic and non-generic classes.
在这篇文章中,我们学习了如何在泛型和非泛型类中定义和使用泛型构造函数。
The full source code can be found over on GitHub.
完整的源代码可以在GitHub上找到。