点击左上角,关注:“锅外的大佬”
专注分享国外最新技术内容
帮助每位开发者更优秀地成长

1.概述

我们之前讨论过 JavaGenerics的基础知识。在本文中,我们将了解Java中的通用构造函数。泛型构造函数是至少需要有一个泛型类型参数的构造函数。我们将看到泛型构造函数并不都是在泛型类中出现的,而且并非所有泛型类中的构造函数都必须是泛型。

2.非泛型类

首先,先写一个简单的类:Entry,它不是泛型类:
  1. publicclassEntry{
  2. privateString data;
  3. privateint rank;
  4. }
在这个类中,我们将添加两个构造函数:一个带有两个参数的基本构造函数和一个通用构造函数。

2.1. 基本构造器

Entry第一个构造函数:带有两个参数的简单构造函数:
  1. publicEntry(String data,int rank){
  2. this.data = data;
  3. this.rank = rank;
  4. }
现在,让我们使用这个基本构造函数来创建一个 Entry对象
  1. @Test
  2. publicvoid givenNonGenericConstructor_whenCreateNonGenericEntry_thenOK(){
  3. Entry entry =newEntry("sample",1);
  4. assertEquals("sample", entry.getData());
  5. assertEquals(1, entry.getRank());
  6. }

2.2. 泛型构造器

接下来,第二个构造器是泛型构造器:
  1. public<E extendsRankable&Serializable>Entry(E element){
  2. this.data = element.toString();
  3. this.rank = element.getRank();
  4. }
虽然 Entry类不是通用的,但它有一个参数为 E的泛型构造函数。
泛型类型 E是受限制的,应该实现 RankableSerializable接口。
现在,让我们看看 Rankable接口,下面是其中一个方法:
  1. publicinterfaceRankable{
  2. publicint getRank();
  3. }
假设我们有一个实现 Rankable接口的类—— Product
  1. publicclassProductimplementsRankable,Serializable{
  2. privateString name;
  3. privatedouble price;
  4. privateint sales;
  5. publicProduct(String name,double price){
  6. this.name = name;
  7. this.price = price;
  8. }
  9. @Override
  10. publicint getRank(){
  11. return sales;
  12. }
  13. }
然后我们可以使用泛型构造函数和 Product创建 Entry对象:
  1. @Test
  2. publicvoid givenGenericConstructor_whenCreateNonGenericEntry_thenOK(){
  3. Product product =newProduct("milk",2.5);
  4. product.setSales(30);
  5. Entry entry =newEntry(product);
  6. assertEquals(product.toString(), entry.getData());
  7. assertEquals(30, entry.getRank());
  8. }

3.泛型类

接下来,我们看一下泛型类: GenericEntry
  1. publicclassGenericEntry<T>{
  2. private T data;
  3. privateint rank;
  4. }
我们将在此类中添加与上一节相同的两种类型的构造函数。

3.1. 基础构造器

首先,让我们为 GenericEntry类编写一个简单的非泛型构造函数:
  1. publicGenericEntry(int rank){
  2. this.rank = rank;
  3. }
尽管 GenericEntry是泛型类,但这是一个简单的,没有任何参数的构造函数。
现在,我们可以使用此构造函数来创建 GenericEntry
  1. @Test
  2. publicvoid givenNonGenericConstructor_whenCreateGenericEntry_thenOK(){
  3. GenericEntry<String> entry =newGenericEntry<String>(1);
  4. assertNull(entry.getData());
  5. assertEquals(1, entry.getRank());
  6. }

3.2. 泛型构造器

接下来,在类中添加第二个构造函数:
  1. publicGenericEntry(T data,int rank){
  2. this.data = data;
  3. this.rank = rank;
  4. }
这是一个泛型构造函数,它有一个泛型类型T的数据参数。注意,我们不需要在构造函数声明中添加,因为它是隐含的。
现在,让我们测试一下通用构造函数:
  1. @Test
  2. publicvoid givenGenericConstructor_whenCreateGenericEntry_thenOK(){
  3. GenericEntry<String> entry =newGenericEntry<String>("sample",1);
  4. assertEquals("sample", entry.getData());
  5. assertEquals(1, entry.getRank());
  6. }

4.不同类型的泛型构造函数

在泛型类中,还有一个构造函数,其泛型类型与类的泛型类型不同:
  1. public<E extendsRankable&Serializable>GenericEntry(E element){
  2. this.data =(T) element;
  3. this.rank = element.getRank();
  4. }
GenericEntry构造函数有类型为 E的参数,该参数与 T类型不同。让我们看看它的实际效果:
  1. @Test
  2. publicvoid givenGenericConstructorWithDifferentType_whenCreateGenericEntry_thenOK(){
  3. Product product =newProduct("milk",2.5);
  4. product.setSales(30);
  5. GenericEntry<Serializable> entry =newGenericEntry<Serializable>(product);
  6. assertEquals(product, entry.getData());
  7. assertEquals(30, entry.getRank());
  8. }
注意:在示例中,我们使用 ProductE创建 SerializableT类型的 GenericEntry,只有当类型 E的参数可以转换为 T时,我们才能使用此构造函数。

5.多种泛类型

接下来,我们有两个泛型类型参数的泛型类 MapEntry
  1. publicclassMapEntry<K, V>{
  2. private K key;
  3. private V value;
  4. publicMapEntry(K key, V value){
  5. this.key = key;
  6. this.value = value;
  7. }
  8. }
MapEntry有一个两个参数的泛型构造函数,每个参数都是不同的类型。让我们用一个简单的单元测试测试一下:
  1. @Test
  2. publicvoid givenGenericConstructor_whenCreateGenericEntryWithTwoTypes_thenOK(){
  3. MapEntry<String,Integer> entry =newMapEntry<String,Integer>("sample",1);
  4. assertEquals("sample", entry.getKey());
  5. assertEquals(1, entry.getValue().intValue());
  6. }

6.通配符

最后,我们可以在泛型构造函数中使用通配符:
  1. publicGenericEntry(Optional<?extendsRankable> optional){
  2. if(optional.isPresent()){
  3. this.data =(T) optional.get();
  4. this.rank = optional.get().getRank();
  5. }
  6. }
在这儿,我们在 GenericEntry构造函数中使用通配符来绑定 Optional类型:
  1. @Test
  2. publicvoid givenGenericConstructorWithWildCard_whenCreateGenericEntry_thenOK(){
  3. Product product =newProduct("milk",2.5);
  4. product.setSales(30);
  5. Optional<Product> optional =Optional.of(product);
  6. GenericEntry<Serializable> entry =newGenericEntry<Serializable>(optional);
  7. assertEquals(product, entry.getData());
  8. assertEquals(30, entry.getRank());
  9. }
请注意,我们应该能够将可选参数类型(Product示例)转换为 GenericEntry类型(Serializable示例)。

7.结束语

在本文中,我们学习了如何在泛型和非泛型类中定义和使用泛型构造函数。完整的源代码可以在 GitHub获取(点击查看原文)。
原文链接:https://www.baeldung.com/java-generic-constructors
作者:baeldung
译者:Emma




点击在看,和我一起帮助更多开发者!
继续阅读
阅读原文