Wie werden Funktionstypen für Void-Methoden (nicht Void) in Java8 angegeben?

Lesezeit: 6 Minuten

Benutzer-Avatar
Valentin

Ich spiele mit Java 8 herum, um herauszufinden, wie es als Bürger erster Klasse funktioniert. Ich habe folgenden Ausschnitt:

package test;

import java.util.*;
import java.util.function.*;

public class Test {

    public static void myForEach(List<Integer> list, Function<Integer, Void> myFunction) {
      list.forEach(functionToBlock(myFunction));
    }

    public static void displayInt(Integer i) {
      System.out.println(i);
    }


    public static void main(String[] args) {
      List<Integer> theList = new ArrayList<>();
      theList.add(1);
      theList.add(2);
      theList.add(3);
      theList.add(4);
      theList.add(5);
      theList.add(6);
      myForEach(theList, Test::displayInt);
    }
}

Was ich versuche, ist die Pass-Methode displayInt zur Methode myForEach Verwenden einer Methodenreferenz. Der Compiler erzeugt den folgenden Fehler:

src/test/Test.java:9: error: cannot find symbol
      list.forEach(functionToBlock(myFunction));
                   ^
  symbol:   method functionToBlock(Function<Integer,Void>)
  location: class Test
src/test/Test.java:25: error: method myForEach in class Test cannot be applied to given ty
pes;
      myForEach(theList, Test::displayInt);
      ^
  required: List<Integer>,Function<Integer,Void>
  found: List<Integer>,Test::displayInt
  reason: argument mismatch; bad return type in method reference
      void cannot be converted to Void

Der Compiler beschwert sich darüber void cannot be converted to Void. Ich weiß nicht, wie ich den Typ der Funktionsschnittstelle in der Signatur von angeben soll myForEach damit der Code kompiliert. Ich weiß, ich könnte einfach den Rückgabetyp von ändern displayInt zu Void und dann zurück null. Es kann jedoch Situationen geben, in denen es nicht möglich ist, die Methode, die ich übergeben möchte, an anderer Stelle zu ändern. Gibt es eine einfache Möglichkeit zur Wiederverwendung? displayInt wie es ist?

Benutzer-Avatar
Edwin Dalorzo

Sie versuchen, den falschen Schnittstellentyp zu verwenden. Der Typ Funktion ist in diesem Fall nicht geeignet, da es einen Parameter erhält und einen Rückgabewert hat. Stattdessen sollten Sie verwenden Verbraucher (früher als Block bekannt)

Der Funktionstyp wird als deklariert

interface Function<T,R> {
  R apply(T t);
}

Der Consumer-Typ ist jedoch kompatibel mit dem, wonach Sie suchen:

interface Consumer<T> {
   void accept(T t);
}

Daher ist Consumer mit Methoden kompatibel, die ein T empfangen und nichts zurückgeben (void). Und das ist, was Sie wollen.

Wenn ich zum Beispiel alle Elemente in einer Liste anzeigen möchte, könnte ich einfach einen Verbraucher dafür mit einem Lambda-Ausdruck erstellen:

List<String> allJedi = asList("Luke","Obiwan","Quigon");
allJedi.forEach( jedi -> System.out.println(jedi) );

Sie können oben sehen, dass in diesem Fall der Lambda-Ausdruck einen Parameter erhält und keinen Rückgabewert hat.

Wenn ich nun anstelle eines Lambda-Ausdrucks eine Methodenreferenz verwenden möchte, um einen Verbrauch dieses Typs zu erstellen, brauche ich eine Methode, die einen String empfängt und void zurückgibt, richtig?.

Ich könnte verschiedene Arten von Methodenreferenzen verwenden, aber in diesem Fall nutzen wir eine Objektmethodenreferenz, indem wir die verwenden println Methode in der System.out Objekt, etwa so:

Consumer<String> block = System.out::println

Oder ich könnte es einfach tun

allJedi.forEach(System.out::println);

Das println -Methode ist geeignet, da sie einen Wert empfängt und einen Rückgabetyp void hat, genau wie die accept Methode im Consumer.

In Ihrem Code müssen Sie also Ihre Methodensignatur in etwa so ändern:

public static void myForEach(List<Integer> list, Consumer<Integer> myBlock) {
   list.forEach(myBlock);
}

Und dann sollten Sie in Ihrem Fall in der Lage sein, einen Verbraucher mit einer statischen Methodenreferenz zu erstellen, indem Sie Folgendes tun:

myForEach(theList, Test::displayInt);

Letztendlich könntest du deine sogar loswerden myForEach Methode zusammen und tun Sie einfach:

theList.forEach(Test::displayInt);

Über Funktionen als Bürger erster Klasse

Alles in allem ist die Wahrheit, dass Java 8 keine Funktionen als erstklassige Bürger haben wird, da der Sprache kein struktureller Funktionstyp hinzugefügt wird. Java wird einfach eine alternative Möglichkeit bieten, Implementierungen funktionaler Schnittstellen aus Lambda-Ausdrücken und Methodenreferenzen zu erstellen. Letztendlich werden Lambda-Ausdrücke und Methodenreferenzen an Objektreferenzen gebunden, daher haben wir nur Objekte als erstklassige Bürger. Wichtig ist, dass die Funktionalität vorhanden ist, da wir Objekte als Parameter übergeben, sie an Variablenreferenzen binden und sie als Werte von anderen Methoden zurückgeben können, dann dienen sie ziemlich genau einem ähnlichen Zweck.

  • Übrigens, Block wurde für geändert Consumer in den neuesten Versionen der JDK 8-API

    – Edwin Dalorzo

    4. Dezember 2013 um 15:45 Uhr

  • Der letzte Absatz beschönigt einige wichtige Fakten: Lambda-Ausdrücke und Methodenreferenzen sind mehr als eine syntaktische Ergänzung. Die JVM selbst stellt neue Typen bereit, um Methodenreferenzen effizienter zu handhaben als mit anonymen Klassen (am wichtigsten MethodHandle) und damit kann der Java-Compiler effizienteren Code erzeugen, z. B. Lambdas ohne Abschluss als statische Methoden implementieren und so die Generierung zusätzlicher Klassen verhindern.

    – Feuermurmel

    17. Dezember 2014 um 12:16 Uhr

  • Und die richtige Version von Function<Void, Void> ist Runnable.

    – Orangenhund

    27. September 2016 um 11:21 Uhr

  • @OrangeDog Das ist nicht ganz richtig. In den Kommentaren zu Runnable und Callable Schnittstellen steht geschrieben, dass sie in Verbindung mit verwendet werden sollen Fäden. Die ärgerliche Folge davon ist, dass sich einige statische Analysetools (wie Sonar) beschweren, wenn Sie anrufen run() Methode direkt.

    – Dennis

    2. November 2016 um 8:20 Uhr

  • Aus reinem Java-Hintergrund frage ich mich, was insbesondere die Fälle sind, in denen Java Funktionen nicht als erstklassige Bürger behandeln kann, selbst nach der Einführung von funktionalen Schnittstellen und Lambdas seit Java8?

    – Vivek Sethi

    4. Juli 2018 um 10:33 Uhr

Benutzer-Avatar
LOAS

Wenn Sie eine Funktion als Argument akzeptieren müssen, die keine Argumente akzeptiert und kein Ergebnis zurückgibt (void), ist es meiner Meinung nach immer noch am besten, so etwas zu haben

  public interface Thunk { void apply(); }

irgendwo in deinem Code. In meinen Kursen zur funktionalen Programmierung wurde das Wort „thunk“ verwendet, um solche Funktionen zu beschreiben. Warum es nicht in java.util.function ist, entzieht sich meinem Verständnis.

In anderen Fällen stelle ich fest, dass selbst wenn java.util.function etwas hat, das mit der von mir gewünschten Signatur übereinstimmt, es sich immer noch nicht immer richtig anfühlt, wenn die Benennung der Schnittstelle nicht mit der Verwendung der Funktion in meinem Code übereinstimmt. Ich denke, es ist ein ähnlicher Punkt, der hier an anderer Stelle in Bezug auf ‘Runnable’ gemacht wird – ein Begriff, der mit der Thread-Klasse verbunden ist – also, obwohl es die Signatur hat, die ich brauche, ist es immer noch wahrscheinlich, dass es den Leser verwirrt.

  • Im Speziellen Runnable erlaubt keine geprüften Ausnahmen, was es für viele Anwendungen unbrauchbar macht. Seit Callable<Void> ist auch nicht nützlich, Sie müssen anscheinend Ihre eigenen definieren. Hässlich.

    – Jesse Glick

    1. August 2017 um 21:14 Uhr

  • In Bezug auf das Problem der überprüften Ausnahmen haben die neuen Funktionen von Java im Allgemeinen damit zu kämpfen. Es ist ein großer Blocker für einige Dinge wie die Stream-API. Sehr schlechtes Design ihrerseits.

    – craigmiller160

    14. Juni 2018 um 14:52 Uhr

Setzen Sie den Rückgabetyp auf Void Anstatt von void und return null

// Modify existing method
public static Void displayInt(Integer i) {
    System.out.println(i);
    return null;
}

ODER

// Or use Lambda
myForEach(theList, i -> {System.out.println(i);return null;});

1349020cookie-checkWie werden Funktionstypen für Void-Methoden (nicht Void) in Java8 angegeben?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy