Void Type in Java – Java中的虚数类型

最后修改: 2019年 5月 5日

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

1. Overview

1.概述

As Java developers, we might have encountered the Void type on some occasions and wondered what was its purpose.

作为Java开发者,我们可能在某些场合遇到过Void类型,并想知道它的用途是什么。

In this quick tutorial, we’ll learn about this peculiar class and see when and how to use it as well as how to avoid using it when possible.

在这个快速教程中,我们将了解这个奇特的类,看看何时和如何使用它,以及如何尽可能地避免使用它。

2. What’s the Void Type

2.什么是Void

Since JDK 1.1, Java provides us with the Void type. Its purpose is simply to represent the void return type as a class and contain a Class<Void> public value. It’s not instantiable as its only constructor is private.

从JDK 1.1开始,Java为我们提供了Void类型。它的目的只是将void返回类型表示为一个类,并包含一个Class<Void>公共值。它是不可实例化的,因为它唯一的构造函数是私有的。

Therefore, the only value we can assign to a Void variable is null. It may seem a little bit useless, but we’ll now see when and how to use this type.

因此,我们可以分配给Void变量的唯一值是null。它可能看起来有点无用,但我们现在将看到何时以及如何使用这种类型。

3. Usages

3.使用方法

There are some situations when using the Void type can be interesting.

有一些情况下,使用Void类型会很有趣。

3.1. Reflection

3.1.反射

First, we could use it when doing reflection. Indeed, the return type of any void method will match the Void.TYPE variable that holds the Class<Void> value mentioned earlier.

首先,我们可以在做反射时使用它。事实上,任何void方法的返回类型将与Void.TYPE变量相匹配,该变量持有前面提到的Class<Void>

Let’s imagine a simple Calculator class:

让我们设想一个简单的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);
    }
}

Some methods are returning an integer, some are not returning anything. Now, let’s say we have to retrieve, by reflection, all methods that don’t return any result. We’ll achieve this by using the Void.TYPE variable:

有些方法会返回一个整数,有些则不返回任何东西。现在,让我们假设我们必须通过反射检索出所有不返回任何结果的方法。我们将通过使用Void.TYPE变量来实现这个目标。

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

As we can see, only the clear() and print() methods have been retrieved.

我们可以看到,只有clear()print()方法被检索到。

3.2. Generics

3.2.仿制药

Another usage of the Void type is with generic classes. Let’s suppose we are calling a method that requires a Callable parameter:

Void类型的另一种用法是用于泛型类。让我们假设我们正在调用一个需要Callable参数的方法。

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

But, the Callable we want to pass doesn’t have to return anything. Therefore, we can pass a Callable<Void>:

但是,我们想要传递的Callable不需要返回任何东西。因此,我们可以传递一个Callable<Void>/em>:

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

As shown above, in order to return from a method with the Void return type, we just have to return null. Moreover, we could have either used a random type (such as Callable<Integer>) and return null or no type at all (Callable), but using Void states our intentions clearly.

如上所示,为了从一个具有Void返回类型的方法中返回,我们只需要返回null。此外,我们可以使用一个随机的类型(如Callable<Integer>)并返回null或者根本没有类型(Callable),但是使用Void明确说明了我们的意图

We can also apply this method to lambdas. As a matter of fact, our Callable could have been written as a lambda. Let’s imagine a method requiring a Function, but we want to use a Function that doesn’t return anything. Then we just have to make it return Void:

我们也可以将这种方法应用于lambdas。事实上,我们的Callable可以被写成lambda。让我们设想一个需要Function的方法,但我们想使用一个不返回任何东西的Function。那么我们只需要让它返回Void

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. How to Avoid Using It?

4.如何避免使用它?

Now, we’ve seen some usages of the Void type. However, even if the first usage is totally fine, we might want to avoid using Void in generics if possible. Indeed, encountering a return type that represents the absence of a result and can only contain null can be cumbersome.

现在,我们已经看到了Void类型的一些用法。然而,即使第一种用法完全没有问题,如果可能的话,我们可能想避免在泛型中使用Void。的确,遇到一个表示没有结果的返回类型,并且只能包含null,会很麻烦。

We’ll now see how to avoid these situations. First, let’s consider our method with the Callable parameter. In order to avoid using a Callable<Void>, we might offer another method taking a Runnable parameter instead:

我们现在来看看如何避免这些情况。首先,让我们考虑一下我们的方法中的Callable参数。为了避免使用Callable<Void>,我们可以提供另一个带Runnable参数的方法:

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

So, we can pass it a Runnable which doesn’t return any value and thus get rid of the useless return null:

因此,我们可以传递给它一个Runnable,它不返回任何值,从而摆脱了无用的return null

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

Defer.defer(runnable);

But then, what if the Defer class is not ours to modify? Then we can either stick to the Callable<Void> option or create another class taking a Runnable and deferring the call to the Defer class:

但是,如果Defer类不是我们可以修改的呢?那么我们可以坚持使用Callable<Void>选项,或者创建另一个采取Runnable的类并将调用推迟到Defer

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

By doing that, we encapsulate the cumbersome part once and for all in our own method, allowing future developers to use a simpler API.

通过这样做,我们将繁琐的部分一劳永逸地封装在我们自己的方法中,使未来的开发者能够使用更简单的API。

Of course, the same can be achieved for Function. In our example, the Function doesn’t return anything, thus we can provide another method taking a Consumer instead:

当然,对于Function也可以实现同样的效果。在我们的例子中,Function并不返回任何东西,因此我们可以提供另一个方法来代替Consumer

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

Then, what if our function doesn’t take any parameters? We can either use a Runnable or create our own functional interface (if that seems clearer):

那么,如果我们的函数不接受任何参数呢?我们可以使用Runnable或者创建我们自己的功能接口(如果这看起来更清晰的话)。

public interface Action {
    void execute();
}

Then, we overload the defer() method again:

然后,我们再次重载defer()方法。

public static void defer(Action action) {
    action.execute();
}
Action action = () -> System.out.println("Hello!");

Defer.defer(action);

5. Conclusion

5.总结

In this short article, we covered the Java Void class. We saw what was its purpose and how to use it. We also learned some alternatives to its usage.

在这篇短文中,我们介绍了Java的Void类。我们看到了它的目的是什么以及如何使用它。我们还学习了一些替代它的用法。

As usual, the full code of this article can be found over on GitHub.

像往常一样,本文的完整代码可以在GitHub上找到over