在API中
"Void类是一个不可实例化的占位符类,用于保存代表Java关键字void的Class对象的引用。"
- "不可实例化的占位符类"是什么意思? 何时使用java.lang.Void? 如果这个类是"不可实例化的",那有什么用处?
java.lang.Void
和void
之间有什么区别?
java.lang.Void
类类似于java.lang.Integer
。 Integer
是对原始类型int
的值进行包装的一种方式。 Void
是对原始类型void
的值进行包装的一种方式。
“但等等,void
没有任何可能的值!”
没错!这就是使得java.lang.Void
“不可实例化”的原因。 :)
Java类型系统的一个很好的特性是每个基本类型都有一个相应的包装类型。 int
有Integer
,long
有Long
,byte
有Byte
...而void
有Void
。如果Void
不存在,那将会很奇怪和不对称。
“那么java.lang.Void
和void
之间有什么区别?”
简单。 void
是原始类型。 Void
是继承自Object
的引用类型。它们在没有任何可能的值的情况下相似;但是从类型系统的角度来看,它们是两种非常不同的类型。
“但我在我的程序中没有任何使用Void
的需求。”
在我的程序中,我也没有用到GarbageCollectorMXBean
。有些特性就没有非晦涩的用途。没关系。
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>
)。
Void
的Map
来模拟Set
的示例和见解,我给予加1。然而,WeakHashMap
中的Void
并不是一个很好的例子,因为这样的构造与普通的HashSet
没有什么区别(没有从“弱”功能中获益)。有趣的是,HashSet<E>
实际上由HashMap<E,Object>
支持。 - Dave HartnollWeakHashMap
对其键具有弱引用(而值则具有强引用),但普通的HashSet
对其键保持强引用。 - JavierVoid 的唯一目的是保存 Void.TYPE
,类似于 void.class
。如果你有对返回值为 void
的方法的反射引用,并且获取其返回类型,则会返回 Void.TYPE
。
你不能也不应该将它用于其他任何事情。
Void
可以与某些使用泛型的类一起使用,例如SwingWorker
。我指出这一点是因为你说Void
不能用于其他任何东西。 - Radu Murzeanull
的类。但更一般来说,Void
类只是你用来找到 Void.TYPE
的地方,它就像一个 Class<void>
。 - Louis WassermanCallable
中抛出一个异常。另外值得注意的是,Callable
中的 call
方法实际上不能是 void 类型,但可以有返回类型为 Void
(因此必须返回 null
),以模拟 void 函数。 - JakobSince: 1.1
。 - Fuad Efendi使用 Void
的另一个例子是 SwingWorker
new SwingWorker<Void, Integer> () {
@Override
protected Void doInBackground(){
...
}
@Override
protected void process(List<Integer> chunk){
...
}
@Override
public void done(){
...
}
}.execute();
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);
}
我个人是这样使用的:
@FunctionalInterface
interface MyPackagePrivateInterface<T, Next> {
//Returns next
Next compareAndSwap(T prev);
}
@FunctionalInterface
public interface ClientPublicInterface<T> extends MyPackagePrivateInterface<T, Void> {
}
我的理由是:
基于用户输入的计算可能会创建新的类型分配,而您的系统可能需要这些分配。
该操作可以保持其名称,因为在客户端看来,它正在执行它所说的内容,但在系统的子层中,等效的操作正在进行,但使用完全不同的类型,这些类型只有您的系统应该知道。
这可能涉及“自相似”原则,即系统由类似于自身的较小组件组成,因此重用具有不同类型和类似名称的接口可能是合乎逻辑的。该系统还意味着具体化(将抽象事物转化为实际)对于系统状态是内在的,因此用户不关心返回值的立即读取,而是依赖于对象状态的变化。
现在,这个特定的例子是针对原子操作的,老实说,我想不出其他需要这种操作的情况,原因是本地原子操作依赖于时间空间(空间=内存范围)快照,以便准确执行。
这些新的分配对于客户端并不重要,而仅对于系统功能重要。
void
是一个关键字,但并非原始类型。实际上,Void.TYPE
被用作表示void
关键字的占位符。 - jilen