将 Scala 单位转换为 Java Void

Convert Scala Unit to Java Void

我有一个 Java class 需要 Function[String, Void],它类似于 Consumer 功能接口。我在 Scala 代码中调用这个 Java 函数。正如您在下面看到的,我在 Scala 方面接受 function: => Unit,因为它等效于 void。当我尝试应用此函数代替 Void return 类型时,会发生类型不匹配的情况。 .asInstanceOf[Void] 的显式转换也引发异常。我们如何将 => Unit 转换为 => Void

// Java 
class AcceptFunction {
   public void temp(Function<String, Void> f) {
      f.apply("Hello");
   }
}

// Scala
def temp(f: String => Unit): Unit = {
   new AcceptFunction().temp(new Function[String, Void]() {
   override def apply(t: String): Void = f(t) // <===== error expecting Void instead of Unit
   })
}

您应该使用 Consumer 而不是 Function 类型,因为您的 Function 没有输出,而 Consumer 用于这种情况。喜欢:

class AcceptFunction {
   public void temp(Consumer<String> f) { // Consumer for no output Function
      f.accept("Hello");
   }
}
def temp(f: String => Unit): Unit = {
   new AcceptFunction().temp(new Consumer[String]() {
   override def accept(t: String): Unit= f(t)    
   })
}

显然,

scala> new jfunc.Acceptor().f(s => println(s))
<console>:12: error: type mismatch;
 found   : Unit
 required: Void
       new jfunc.Acceptor().f(s => println(s))
                                          ^

scala> new jfunc.Acceptor().f { s => println(s) ; null }
hello, world.

其中 java 与您显示的一样:

package jfunc;

import java.util.function.*;

public class Acceptor {
  public void f(Function<String, Void> g) { g.apply("hello, world."); }
}

确实如此:

scala> :javap -c -
  public static final java.lang.Void $anonfun$res0(java.lang.String);
    Code:
       0: getstatic     #33                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
       3: aload_0
       4: invokevirtual #37                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
       7: aconst_null
       8: areturn

  public $line3.$read$$iw$$iw$();
    Code:
       0: aload_0
       1: invokespecial #39                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: putstatic     #41                 // Field MODULE$:L$line3/$read$$iw$$iw$;
       8: aload_0
       9: new           #43                 // class jfunc/Acceptor
      12: dup
      13: invokespecial #44                 // Method jfunc/Acceptor."<init>":()V
      16: invokedynamic #63,  0             // InvokeDynamic #0:apply:()Ljava/util/function/Function;
      21: invokevirtual #67                 // Method jfunc/Acceptor.f:(Ljava/util/function/Function;)V
      24: getstatic     #72                 // Field scala/runtime/BoxedUnit.UNIT:Lscala/runtime/BoxedUnit;
      27: putfield      #74                 // Field res0:Lscala/runtime/BoxedUnit;
      30: return

I have a Java class which takes Function[String, Void], which is similar to Consumer functional interface.

那么这个class应该使用Consumer。初步估计,Function<String, Void> 永远不是正确的类型。如果你不能改变它,至少要意识到它是一个糟糕的设计并小心使用这个库。 Som-snytt 的回答已经展示了如何从 Scala 中正确调用 temp,如果需要,编写从 String => UnitFunction[String, Void] 的隐式转换也很简单。所以这个答案的其余部分是关于为什么你的其他尝试没有奏效的原因。

as it is void equivalent

准确地说:相当于void,而不是Void。等效的 Java 代码将调用 void 方法,例如Consumer.accept:

public void tempOfVoid(Consumer<String> f) {
    temp(new Function<String, Void> {
        public Void apply(String x) {
            return f.accept(x);
        }
    };
}

并且将无法进行类型检查。相反,你需要

        public Void apply(String x) {
            f.accept(x);
            return null;
        }

与 som-snytt 的回答在 Scala 中显示的相同。

现在,您可以问:当 Unit 是类型参数时会发生什么,因为 void 不能,或者当类型 Unit 的值需要表示时在运行时(一个例子是 val x: Any = ())?在这些情况下,Unitscala.runtime.BoxedUnit class 表示,就像 Int 通常被翻译成 int 但有时被翻译成 java.lang.Integer 一样。这解释了为什么强制转换为 Void 会引发异常(并且不会导致编译错误)。 Void 不是适合这些情况的翻译,因为它没有(非 null)值,而 Unit 只有一个值:().