1. 概述

在本教程中,我们将深入探讨Java中的Holder<T>类。尽管它不是Java的内置类,但Holder<T>概念可以极大地提高我们的开发效率。让我们了解Holder<T>的强大之处以及它是如何提升代码的。

2. 值传递语义的局限性

要理解为何我们需要Holder<T>类,首先考虑一个常见场景:向方法传递一个简单的Boolean。假设我们有一个模拟服务方法getSupplierByZipCode(),期望它修改Boolean值:

public class SupplierService {
    public void getSupplierByZipCode(String zip, Boolean result) {
        if (zip.startsWith("9")) {
            result = true;
        } else {
            result = false;
        }
    }
}

现在,我们用一个以“9”开头的zipCode来测试这个服务,期望result变为true

@Test
public void givenValidZipCode_whenGetSupplierByZipCode_thenTrue() {
    SupplierService service = new SupplierService();
    Boolean result = false;
    String zipCode = "98682";
    service.getSupplierByZipCode(zipCode, result);
    assertTrue(result);
}

这个测试失败了!由于Java的值传递(/java-pass-by-value-or-pass-by-reference)语义,我们在getSupplierByZipCode()方法中传递的result布尔值实际上并未被该方法修改。当方法尝试修改result时,它只是在操作一个副本,原始的result保持不变。

这就是Holder<T>可以帮助我们解决的问题。

3. 设计Holder<T>的概念

我们可以将Holder<T>视为一个泛型容器或包装类,能够存储和管理任何类型T的对象。

它的主要存在是为了克服Java的值传递语义,提供了一种间接的方式来模拟引用传递行为。

这里的T是一个类型参数,意味着任何有效的Java引用类型都可以替换它。这使得我们只需要一个Holder类就可以适应任何数据类型。但需要注意的是,并非所有情况下都应随意使用Holder<T>。特别是处理不可变对象时,使用Holder<T>可能不是最高效或推荐的方法。

4. Holder<T>

假设我们有一个简单的Holder类,用于封装类型为T的值。以下是定义它的样子:

public class Holder<T> {
    public T value;

    public Holder(T value) {
        this.value = value;
    }
}

在这个例子中,Holder<T>作为一个容器,用于持有并管理任何类型T的值。

5. 使用Holder<T>

现在,让我们调整SupplierService,以解决之前Java值传递语义带来的限制。我们不再直接将Boolean传递给getSupplierByZipCode()方法,而是使用Holder<T>类。这样,方法就可以修改Holdervalue,模拟一种需要从方法返回额外信息的情况,而不仅仅是其返回值。

public class SupplierService {
    public void getSupplierByZipCode(String zip, Holder<Boolean> resultHolder) {
        if (zip.startsWith("9")) {
            resultHolder.value = true;
        } else {
            resultHolder.value = false;
        }
    }
}

现在,我们使用修改后的SupplierServiceHolder<T>重运行测试。

@Test
public void givenValidZipCode_whenGetSupplierByZipCode_thenTrue() {
    SupplierService service = new SupplierService();
    Holder<Boolean> resultHolder = new Holder<>(false);
    String zipCode = "98682";
    service.getSupplierByZipCode(zipCode, resultHolder);
    assertTrue(resultHolder.value);
}

@Test
public void givenInvalidZipCode_whenGetSupplierByZipCode_thenFalse() {
    SupplierService service = new SupplierService();
    Holder<Boolean> resultHolder = new Holder<>(true);
    String zipCode = "12345";
    service.getSupplierByZipCode(zipCode, resultHolder);
    assertFalse(resultHolder.value);
}

这次,测试通过了。Holder<T>类通过提供一层额外的间接性,允许我们在getSupplierByZipCode()方法中模拟引用传递的特性,从而修改我们想要的变量。

6. 总结

在这篇文章中,我们了解到Holder<T>类在Java编程中可以作为灵活且强大的工具,应用于各种场景。虽然它不是一个内置类,但Holder<T>概念为我们提供了创建灵活和可重用代码的方式,能够处理不同类型的对象。这使我们在某些情况下能够克服Java值传递语义的局限性。

如往常一样,这些示例的代码可以在GitHub上找到。