Java泛型建構函式

鍋外的大佬發表於2019-05-29

1.概述

我們之前討論過Java Generics的基礎知識。在本文中,我們將瞭解Java中的通用建構函式。 泛型建構函式是至少需要有一個泛型型別引數的建構函式。我們將看到泛型建構函式並不都是在泛型類中出現的,而且並非所有泛型類中的建構函式都必須是泛型。

2.非泛型類

首先,先寫一個簡單的類:Entry,它不是泛型類:

public class Entry {
    private String data;
    private int rank;
}
複製程式碼

在這個類中,我們將新增兩個建構函式:一個帶有兩個引數的基本建構函式和一個通用建構函式。

2.1 基本構造器

Entry第一個建構函式:帶有兩個引數的簡單建構函式:

public Entry(String data, int rank) {
    this.data = data;
    this.rank = rank;
}
複製程式碼

現在,讓我們使用這個基本建構函式來建立一個Entry物件

@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();
}
複製程式碼

雖然Entry類不是通用的,但它有一個引數為E的泛型建構函式。

泛型型別E是受限制的,應該實現RankableSerializable介面。

現在,讓我們看看Rankable介面,下面是其中一個方法:

public interface Rankable {
    public int getRank();
}
複製程式碼

假設我們有一個實現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;
    }
}
複製程式碼

然後我們可以使用泛型建構函式和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.泛型類

接下來,我們看一下泛型類:GenericEntry

public class GenericEntry<T> {
    private T data;
    private int rank;
}
複製程式碼

我們將在此類中新增與上一節相同的兩種型別的建構函式。

3.1 基礎構造器

首先,讓我們為GenericEntry類編寫一個簡單的非泛型建構函式:

public GenericEntry(int rank) {
    this.rank = rank;
}
複製程式碼

儘管GenericEntry是泛型類,但這是一個簡單的,沒有任何引數的建構函式。

現在,我們可以使用此建構函式來建立GenericEntry

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

    assertNull(entry.getData());
    assertEquals(1, entry.getRank());
}
複製程式碼

3.2 泛型構造器

接下來,在類中新增第二個建構函式:

public GenericEntry(T data, int rank) {
    this.data = data;
    this.rank = rank;
}
複製程式碼

這是一個泛型建構函式,它有一個泛型型別T的資料引數。注意,我們不需要在建構函式宣告中新增,因為它是隱含的。

現在,讓我們測試一下通用建構函式:

@Test
public void givenGenericConstructor_whenCreateGenericEntry_thenOK() {
    GenericEntry<String> entry = new GenericEntry<String>("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();
}
複製程式碼

GenericEntry建構函式有型別為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());
}
複製程式碼

注意:在示例中,我們使用Product(E)建立Serializable(T)型別的GenericEntry,只有當型別E的引數可以轉換為T時,我們才能使用此建構函式。

5.多種泛型別

接下來,我們有兩個泛型型別引數的泛型類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有一個兩個引數的泛型建構函式,每個引數都是不同的型別。讓我們用一個簡單的單元測試測試一下:

@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.萬用字元

最後,我們可以在泛型建構函式中使用萬用字元:

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

在這兒,我們在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());
}
複製程式碼

請注意,我們應該能夠將可選引數型別(Product示例)轉換為GenericEntry型別(Serializable示例)。

7.結束語

在本文中,我們學習瞭如何在泛型和非泛型類中定義和使用泛型建構函式。

完整的原始碼可以在GitHub獲取(點選檢視原文)。

原文連結:www.baeldung.com/java-generi…

作者:baeldung

譯者:Emma

推薦關注公眾號:鍋外的大佬

每日推送國外優秀的技術翻譯文章,勵志幫助國內的開發者更好地成長!

 



相關文章