1. 概述

作为 Java 开发者,你可能在某些场景下见过 Void 类型,但又不确定它的用途。

在这篇简短的文章中,我们将了解这个“奇怪”的类,探讨它在什么场景下有用,如何使用,以及在某些情况下如何避免使用它。

2. 什么是 Void 类型

从 JDK 1.1 开始,Java 提供了 Void 类型。✅

它的作用是将 void 返回类型封装为一个类,并提供一个 Class<Void> 类型的常量。

需要注意的是:

  • Void 类不能被实例化(构造函数是私有的)
  • 唯一能赋值给 Void 变量的就是 null

看起来它似乎没什么用,但在特定场景下还是有其用途的。

3. 使用场景

Void 类型在以下两个场景中比较有用:

3.1. 反射(Reflection)

在反射中,我们可以通过 Void.TYPE 来判断一个方法是否返回 void

比如我们有如下 Calculator 类:

public class Calculator {
    private int result = 0;

    public int add(int number) {
        return result += number;
    }

    public int sub(int number) {
        return result -= number;
    }

    public void clear() {
        result = 0;
    }

    public void print() {
        System.out.println(result);
    }
}

如果我们想通过反射找出所有返回类型为 void 的方法,可以这样做:

@Test
void givenCalculator_whenGettingVoidMethodsByReflection_thenOnlyClearAndPrint() {
    Method[] calculatorMethods = Calculator.class.getDeclaredMethods();
    List<Method> calculatorVoidMethods = Arrays.stream(calculatorMethods)
      .filter(method -> method.getReturnType().equals(Void.TYPE))
      .collect(Collectors.toList());

    assertThat(calculatorVoidMethods)
      .allMatch(method -> Arrays.asList("clear", "print").contains(method.getName()));
}

测试结果会只包含 clear()print() 方法,说明我们成功筛选出了返回类型为 void 的方法。

3.2. 泛型(Generics)

另一个使用场景是泛型编程。比如我们有如下工具类:

public class Defer {
    public static <V> V defer(Callable<V> callable) throws Exception {
        return callable.call();
    }
}

如果我们想传入一个不返回值的 Callable,可以使用 Callable<Void>

@Test
void givenVoidCallable_whenDiffer_thenReturnNull() throws Exception {
    Callable<Void> callable = new Callable<Void>() {
        @Override
        public Void call() {
            System.out.println("Hello!");
            return null;
        }
    };

    assertThat(Defer.defer(callable)).isNull();
}

✅ 使用 Void 可以明确表示我们不期望返回值,避免使用 Callable<Integer> 或原始类型 Callable 等模糊表达。

同样的逻辑也适用于 Function

public static <T, R> R defer(Function<T, R> function, T arg) {
    return function.apply(arg);
}

我们可以这样使用:

@Test
void givenVoidFunction_whenDiffer_thenReturnNull() {
    Function<String, Void> function = s -> {
        System.out.println("Hello " + s + "!");
        return null;
    };

    assertThat(Defer.defer(function, "World")).isNull();
}

4. 如何避免使用 Void

虽然 Void 在某些泛型场景下有用,但 在大多数情况下我们应尽量避免使用它,因为它带来的 null 返回值容易引发困惑。

替代方案一:使用 Runnable

比如上面的 defer(Callable<Void>) 方法,我们可以提供一个接受 Runnable 的重载:

public static void defer(Runnable runnable) {
    runnable.run();
}

这样调用者就无需再写 return null

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello!");
    }
};

Defer.defer(runnable);

替代方案二:自定义函数式接口

如果原类无法修改,可以封装一个适配器类:

public class MyOwnDefer {
    public static void defer(Runnable runnable) throws Exception {
        Defer.defer(new Callable<Void>() {
            @Override
            public Void call() {
                runnable.run();
                return null;
            }
        });
    }
}

这样,Void 的使用就被封装在内部,外部 API 更加清晰。

替代方案三:使用 Consumer

对于不返回值的 Function,可以用 Consumer 替代:

public static <T> void defer(Consumer<T> consumer, T arg) {
    consumer.accept(arg);
}

如果函数没有参数,还可以自定义一个 Action 接口:

public interface Action {
    void execute();
}

然后提供一个重载:

public static void defer(Action action) {
    action.execute();
}

调用示例:

Action action = () -> System.out.println("Hello!");

Defer.defer(action);

5. 总结

在这篇文章中,我们了解了 Java 中的 Void 类型:

  • 它是一个特殊的类,用于表示 void 返回类型
  • 主要用于反射和泛型编程中
  • 但在实际开发中应尽量避免使用它,优先使用 RunnableConsumer 等更清晰的替代方案

如果你在项目中看到 Void 类型的使用,不妨思考一下是否有更优雅的替代方式。这样不仅代码更清晰,也更容易维护。

完整示例代码可以参考 GitHub 仓库


原始标题:Void Type in Java