1. 概述

我们之前已经聊过 Java 泛型 的基础知识。本篇我们将深入讲解 Java 中的 泛型构造函数(Generic Constructor)

泛型构造函数是指构造函数中至少有一个参数是泛型类型。

重点:

  • 泛型构造函数不一定要出现在泛型类中;
  • 泛型类中的构造函数也不一定都是泛型的。

2. 非泛型类中的泛型构造函数

我们先来看一个非泛型类 Entry

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

这个类不是泛型类,但我们可以在其中定义两个构造函数:一个是普通构造函数,另一个是泛型构造函数。

2.1. 基础构造函数

第一个构造函数是一个简单的双参数构造函数:

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

测试代码如下:

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

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

2.2. 泛型构造函数

接下来我们添加一个泛型构造函数:

public <E extends Rankable & Serializable> Entry(E element) {
    this.data = element.toString();
    this.rank = element.getRank();
}

这个构造函数使用了泛型类型 E,并要求它同时实现 RankableSerializable 接口。

我们先定义 Rankable 接口:

public interface Rankable {
    int getRank();
}

然后定义一个 Product 类,它实现了 RankableSerializable 接口:

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;
    }
}

接着就可以用这个泛型构造函数来创建 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. 泛型类中的构造函数

我们再来看一个泛型类 GenericEntry<T>

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

同样地,我们也可以在这个类中添加普通构造函数和泛型构造函数。

3.1. 基础构造函数

这是一个非泛型构造函数:

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

虽然类是泛型的,但这个构造函数没有使用泛型参数,因此不是泛型构造函数。

测试代码如下:

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

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

3.2. 泛型构造函数

接下来是使用泛型参数的构造函数:

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

注意:虽然构造函数使用了泛型参数 T,但不需要在构造函数前加 <T>,因为它是类泛型参数的延续。

测试代码如下:

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

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

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

在泛型类中,构造函数的泛型类型也可以和类的泛型类型不同:

public <E extends Rankable & Serializable> GenericEntry(E element) {
    this.data = (T) element;
    this.rank = element.getRank();
}

这个构造函数使用了泛型类型 E,它和类的泛型 T 是不同的类型。

测试代码如下:

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

    GenericEntry<Serializable> entry = new GenericEntry<>(product);

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

⚠️ 注意:这种用法需要确保 E 能够安全地转换为 T,否则会抛出异常。

5. 多泛型类型的构造函数

我们再来看一个包含两个泛型类型的类 MapEntry<K, V>

public class MapEntry<K, V> {
    private K key;
    private V value;

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

这个构造函数使用了两个不同类型的泛型参数。

测试代码如下:

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

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

6. 使用通配符的泛型构造函数

我们还可以在泛型构造函数中使用通配符:

public GenericEntry(Optional<? extends Rankable> optional) {
    if (optional.isPresent()) {
        this.data = (T) optional.get();
        this.rank = optional.get().getRank();
    }
}

该构造函数接受一个 Optional<? extends Rankable> 参数,使用通配符限定了类型范围。

测试代码如下:

@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<>(optional);

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

⚠️ 注意:这种用法需要确保 Optional 中的类型可以被转换为类的泛型类型。

7. 小结

本文总结:

  • 泛型构造函数允许我们在构造函数中使用泛型类型;
  • 它可以存在于非泛型类中;
  • 泛型构造函数的泛型类型可以与类的泛型类型不同;
  • 可以使用通配符来增强泛型构造函数的灵活性;
  • 构造函数中可以使用多个泛型类型。

完整代码可在 GitHub 获取。


原始标题:Generic Constructors in Java | Baeldung