Generic Constructors in Java – Java中的通用构造器

最后修改: 2019年 4月 21日

中文/混合/英文(键盘快捷键:t)

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是有边界的,应该实现RankableSerializable接口。

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上找到。