Преобразование модуля Scala в Java Void

У меня есть класс Java, который принимает Function[String, Void], что похоже на функциональный интерфейс Consumer. Я вызываю эту функцию Java из кода Scala. Как вы можете видеть ниже, я принимаю function: => Unit на стороне Scala, поскольку это недействительный эквивалент. Когда я пытаюсь применить эту функцию вместо возвращаемого типа Void, происходит несоответствие типов. Явное приведение .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
   })
}

person user_1357    schedule 25.05.2017    source источник
comment
Вы имели в виду .asInstanceOf[(String) => Void]?   -  person Aluan Haddad    schedule 25.05.2017
comment
cf stackoverflow.com/questions/39735812/   -  person som-snytt    schedule 25.05.2017
comment
У меня есть класс Java, который принимает Function[String, Void], который похож на функциональный интерфейс Consumer. Тогда этот класс должен использовать Consumer. Если вы не можете изменить это, по крайней мере, знайте, что это плохой дизайн, и будьте осторожны с этой библиотекой. поскольку это недействительный эквивалент Да, это эквивалентно void, а не Void. Таким образом, вы получаете несоответствие типов, как если бы вы использовали метод Java, возвращающий void.   -  person Alexey Romanov    schedule 25.05.2017
comment
@AlexeyRomanov Я хотел бы понять интероперабельность, но я слишком ленив, т.к. мне это не нужно. Я подозреваю, что string =› unit должно быть легко. Может быть, ваш ответ должен быть каноническим?   -  person som-snytt    schedule 25.05.2017
comment
@som-snytt Я расширил его до ответа.   -  person Alexey Romanov    schedule 26.05.2017


Ответы (3)


По всей видимости,

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$1(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
person som-snytt    schedule 25.05.2017

Вы должны использовать 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)    
   })
}
person chengpohi    schedule 25.05.2017
comment
Спасибо, что заметили это. Мне было любопытно, почему вышеуказанная реализация не сработала. - person user_1357; 25.05.2017

У меня есть класс Java, который принимает Function[String, Void], что похоже на функциональный интерфейс Consumer.

Тогда этот класс должен использовать Consumer. В первом приближении Function<String, Void> никогда не подходит для использования. Если вы не можете изменить это, по крайней мере, знайте, что это плохой дизайн, и будьте осторожны с этой библиотекой. Ответ Som-snytt уже показывает, как правильно вызывать temp из Scala, и при желании просто написать неявное преобразование из String => Unit в Function[String, Void]. Итак, остальная часть этого ответа посвящена тому, почему другие ваши попытки не сработали.

поскольку это недействительный эквивалент

Точно: это эквивалентно 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;
        }

так же, как показано в Scala ответом som-snytt.

Теперь вы можете спросить: что происходит, когда Unit является аргументом типа, поскольку void не может быть, или когда значение типа Unit должно быть представлено во время выполнения (например, val x: Any = ())? В этих случаях Unit вместо этого представлен классом scala.runtime.BoxedUnit, точно так же, как Int обычно переводится в int, но иногда и в java.lang.Integer. Это объясняет, почему приведение к Void вызывает исключение (и не приводит к ошибке компиляции). Void не является подходящим переводом для этих случаев, потому что у него нет значений (отличных от null), в то время как Unit имеет ровно одно: ().

person Alexey Romanov    schedule 25.05.2017