java.lang.Void和void有什么区别?

82

在API中

"Void类是一个不可实例化的占位符类,用于保存代表Java关键字void的Class对象的引用。"

  1. "不可实例化的占位符类"是什么意思? 何时使用java.lang.Void? 如果这个类是"不可实例化的",那有什么用处?
  2. java.lang.Voidvoid之间有什么区别?
7个回答

114

java.lang.Void类类似于java.lang.IntegerInteger是对原始类型int的值进行包装的一种方式。 Void是对原始类型void的值进行包装的一种方式。

“但等等,void没有任何可能的值!”

没错!这就是使得java.lang.Void“不可实例化”的原因。 :)

Java类型系统的一个很好的特性是每个基本类型都有一个相应的包装类型。 intIntegerlongLongbyteByte...而voidVoid。如果Void不存在,那将会很奇怪和不对称。

“那么java.lang.Voidvoid之间有什么区别?”

简单。 void是原始类型。 Void是继承自Object的引用类型。它们在没有任何可能的值的情况下相似;但是从类型系统的角度来看,它们是两种非常不同的类型。

“但我在我的程序中没有任何使用Void的需求。”

在我的程序中,我也没有用到GarbageCollectorMXBean。有些特性就没有非晦涩的用途。没关系。


3
void是一个关键字,但并非原始类型。实际上,Void.TYPE被用作表示void关键字的占位符。 - jilen
java.lang.Void类类似于java.lang.Integer类,如果是这样的话,我就可以将Void赋值给一个“void”方法。与其他基本类型包装类相比,Void显然是一个例外。 - wilmol

77

Void 最常见的用途是反射,但这并不是它唯一的使用场景。

void 是一个关键字,表示函数不返回值。

java.lang.Void 是一个引用类型,因此以下内容是有效的:

 Void nil = null;

作为返回类型(具有返回值类型为Void的函数),这意味着该函数*始终*返回null(它不能返回除null以外的任何内容,因为Void没有实例)。

(到目前为止还不够有趣...)

 Void function(int a, int b) {
    //do something
    return null;
 }

为什么我需要一个总是返回 null 的函数?

在泛型出现之前,我没有使用 Void 的用例。

有了泛型,就有了一些有趣的情况。例如,Future<T> 是另一个线程执行的异步操作结果的持有者。 Future.get 将返回操作值(类型为 T),并阻塞直到计算完成。

但是...如果没有需要返回的内容怎么办?很简单:使用 Future<Void>。例如,在Google App Engine中,异步数据存储服务delete操作返回一个future。当get()在该future上调用时,删除完成后将返回null。可以使用Callables编写类似的示例。

另一个用例是没有值的Map,即Map<T,Void>。这样的map的行为类似于Set<T>,当没有等效的Set实现时可能会很有用(例如,没有WeakHashSet,那么可以使用WeakHashMap<T,Void>)。


1
这是一个很好的解释:本质上,Void 是一种静态强制执行类型绝对不可能返回任何东西的方式。它比例如 WeakHashMap<T,Object> 更加明确,否则你就必须使用它。 - jayunit100
2
对于使用包含VoidMap来模拟Set的示例和见解,我给予加1。然而,WeakHashMap中的Void并不是一个很好的例子,因为这样的构造与普通的HashSet没有什么区别(没有从“弱”功能中获益)。有趣的是,HashSet<E>实际上由HashMap<E,Object>支持。 - Dave Hartnoll
1
@Dave,实际上它们是不同的,因为WeakHashMap对其键具有弱引用(而值则具有强引用),但普通的HashSet对其键保持强引用。 - Javier
@Javier 是的,你说得对。我错误地认为这些值是弱引用。我的错。 - Dave Hartnoll

60

Void 的唯一目的是保存 Void.TYPE,类似于 void.class。如果你有对返回值为 void 的方法的反射引用,并且获取其返回类型,则会返回 Void.TYPE

你不能也不应该将它用于其他任何事情。


12
Void可以与某些使用泛型的类一起使用,例如SwingWorker。我指出这一点是因为你说Void不能用于其他任何东西。 - Radu Murzea
1
这是正确的;它通常可以替代一个不应有任何实例除了 null 的类。但更一般来说,Void 类只是你用来找到 Void.TYPE 的地方,它就像一个 Class<void> - Louis Wasserman
我认为这个答案经不起审查。我在下面添加了一个Void可能使用的示例区域。 - John Deverall
您也可以使用Void来创建和提交返回void的Callable实现。虽然使用Runnable可能更好,但您可能需要将其提交到接收Callables的通用实现中。 - WillD
1
@WillD 或许你需要从 Callable 中抛出一个异常。另外值得注意的是,Callable 中的 call 方法实际上不能是 void 类型,但可以有返回类型为 Void(因此必须返回 null),以模拟 void 函数。 - Jakob
显示剩余2条评论

5
Void是自动装箱特性(JDK 1.5以后),用于void类型。
很明显,Void是引用类型,而void是原始类型。
那么,什么时候需要使用Void呢?一个常见的情况是在泛型类型中,我们无法使用原始类型。比如,在Android中,如果我不想获取进度更新,我不能使用void(原始类型),而需要使用java.lang.Void,例如AsyncTask。

JavaDocs 中提到 Void Since: 1.1 - Fuad Efendi

2

使用 Void 的另一个例子是 SwingWorker

new SwingWorker<Void, Integer> () {
    @Override
    protected Void doInBackground(){
        ...
    }
    @Override
    protected void process(List<Integer> chunk){
        ...
    }
    @Override
    public void done(){
        ...
    }
}.execute();

1

Void非常有用,因为有时需要在方法外部指定方法的返回类型。

例如,考虑这个java 8 lambda expression,它使用名为checkBenefitConcertInCentralPark的方法来检查EventResource对象是否具有某些属性,并将该方法传递到checkCreatedEvent方法中:

eventChecker.checkCreatedEvent(TestEvents::checkBenefitConcertInCentralPark);

checkBenefitConcertInCentralPark 方法的定义如下(注意使用了 Void):

    public static Void checkBenefitConcertInCentralPark(EventResource eventResource) { 
        // JUnit code here...
        // assertThat(blablabla  :)  )

        return null; // we can only return null at the end of a method when returning Void
    }

然后将名为checkBenefitConcertInCentralPark的方法传递给checkCreatedEvent方法。
    // Function<EventResource, Void> describes the checkBenefitConcertInCentralPark method
    public void checkCreatedEvent(Function<EventResource, Void> function) { 
        function.apply(this.eventResource);
    }

你没有解释为什么需要返回Void的函数而不是使用Consumer。 - Louis Wasserman

0

我个人是这样使用的:

@FunctionalInterface
interface MyPackagePrivateInterface<T, Next> {
   //Returns next
   Next compareAndSwap(T prev);
}

@FunctionalInterface
public interface ClientPublicInterface<T> extends MyPackagePrivateInterface<T, Void> {
}

我的理由是:

基于用户输入的计算可能会创建新的类型分配,而您的系统可能需要这些分配。

该操作可以保持其名称,因为在客户端看来,它正在执行它所说的内容,但在系统的子层中,等效的操作正在进行,但使用完全不同的类型,这些类型只有您的系统应该知道。

这可能涉及“自相似”原则,即系统由类似于自身的较小组件组成,因此重用具有不同类型和类似名称的接口可能是合乎逻辑的。该系统还意味着具体化(将抽象事物转化为实际)对于系统状态是内在的,因此用户不关心返回值的立即读取,而是依赖于对象状态的变化。

现在,这个特定的例子是针对原子操作的,老实说,我想不出其他需要这种操作的情况,原因是本地原子操作依赖于时间空间(空间=内存范围)快照,以便准确执行。

这些新的分配对于客户端并不重要,而仅对于系统功能重要。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接