[JAVA] Ein Überprüfungshinweis zur Funktionsoberfläche

Überblick

Dies ist eine Überprüfung der in Java 1.8 eingeführten Funktionsschnittstelle.

Umgebung

Referenz

Eine einfache Überprüfung des Schreibens

In diesem Code empfängt die forEach-Methode eine Consumer-Funktionsschnittstelle. Dieses Schreiben ist aber vereinfacht

List<String> list = List.of("a", "b", "c");
list.forEach(System.out::println);

Wenn Sie Java 1.8 oder früher schreiben, ist dies wie folgt.

public class PrintLine implements Consumer<Object> {
	@Override
	public void accept(Object t) {
		System.out.println(t);
	}
}
List<String> list = List.of("a", "b", "c");
PrintLine println = new PrintLine();
list.forEach(println);

Wenn Sie eine anonyme Klasse schreiben, sieht diese wie folgt aus.

List<String> list = List.of("a", "b", "c");
list.forEach(new Consumer<Object>() {
	@Override
	public void accept(Object t) {
		System.out.println(t);
	}
});

Diese Schreibstile können mithilfe der in Java 1.8 eingeführten Lambda-Ausdrücke vereinfacht werden.

// (1)Wenn Sie den Schreibstil der anonymen Klasse in Lambda-Ausdruck ändern, können Sie ihn so schreiben.
list.forEach((String t) -> {
	System.out.println(t);
});

// (2)Der Argumenttyp ist selbsterklärend und kann weggelassen werden.
list.forEach((t) -> {
	System.out.println(t);
});

// (3)Wenn es nur ein Argument gibt, das Argument()Kann ausgelassen werden.
list.forEach(t -> {
	System.out.println(t);
});

// (4)Wenn der Code im Block eine Zeile ist{}Kann ausgelassen werden. Sie benötigen nicht einmal ein Semikolon in Ihrem Code.
list.forEach(t -> System.out.println(t));

// (5)Abstrakte Methode der funktionalen Schnittstelle (in diesem Fall Consumer).akzeptieren) Unterschrift und
//Wenn die Signaturen der auszuführenden Methoden übereinstimmen, kann sie als Methodenreferenz geschrieben werden.
list.forEach(System.out::println);

FunctionalInterface Annotation

Funktionsschnittstellen werden mit "@ FunctionalInterface" kommentiert.

@FunctionalInterface
public interface Consumer<T> {
  //...
}

Ein informativer Annotationstyp, der angibt, dass eine Schnittstellentypdeklaration eine funktionale Schnittstelle im Sinne der Java-Sprachspezifikation sein soll. Konzeptionell hat eine funktionale Schnittstelle nur eine abstrakte Methode.

Wie oben zitiert, ist die Bedingung, die die Definition einer funktionalen Schnittstelle erfüllt, dass die Schnittstelle nur eine abstrakte Methode deklariert hat.

Unabhängig davon, ob der Annotationstyp FunctionalInterface in der Schnittstellendeklaration vorhanden ist, behandelt der Compiler jede Schnittstelle, die der Definition einer Funktionsschnittstelle entspricht, als Funktionsschnittstelle.

Wenn diese Bedingung erfüllt ist, wird der Compiler als funktionale Schnittstelle mit oder ohne diese Anmerkung behandelt.

Paket java.util.function

Dieses Paket definiert eine grundlegende Funktionsschnittstelle. Typische Beispiele sind "Consumer", "Function", "Predicate" und "Supplier", und es gibt auch "IntCounsumer" und "IntFunction", die spezielle Versionen davon sind. Es gibt auch funktionale Schnittstellen, die für bestimmte Zwecke außerhalb dieses Pakets implementiert sind.

Verbraucher (Single-Term-Funktion von T bis void)

Parameter eingeben: Eingabetyp T-Operation

Abstrakte Methode


void accept(T t);

BiConsumer Es gibt auch eine funktionale Schnittstelle namens "BiConsumer", die zwei Argumente akzeptiert, nämlich eine Spezialisierung von "Consumer".

Parameter eingeben: T - Art des ersten Operationsarguments U - Typ des zweiten Operationsarguments

Abstrakte Methode


void accept​(T t, U u);

Anwendungsbeispiel in JDK

java.base : java.util.Iterator<E>

forEach

Eine der häufigsten Methoden zur Verwendung von Consumer ist die forEach-Methode des Iterators.

Iterable.forEach


default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}

Stichprobe Schreiben Sie seit Java 1.8 nicht wie (1), sondern wie (2) oder (3) mit einem Lambda-Ausdruck. Wenn Sie eine Zeile wie (2) verwenden möchten, müssen Sie sie nicht in einen Block einschließen, und Sie können wie (3) schreiben. Darüber hinaus kann der Code vereinfacht werden, indem er wie in (4) als Methodenreferenz geschrieben wird.

List<String> list = List.of("a", "b", "c");

// (1)
list.forEach(new Consumer<String>() {
	@Override
	public void accept(String t) {
		System.out.println(t);
	}
});

// (2)
list.forEach(s -> {
	System.out.println(s);
});

// (3)
list.forEach(s -> System.out.println(s));

// (4)Methodenreferenz
list.forEach(System.out::println);

Funktion (Einzeltermfunktion von T nach R)

Parameter eingeben: T-Funktion Eingangstyp R - Funktionsergebnistyp

Abstrakte Methode


R apply(T t);

BiFunction Es gibt auch eine funktionale Schnittstelle namens "BiFunction", die zwei Argumente akzeptiert, nämlich eine Spezialisierung von "Function".

Parameter eingeben: T - Typ des ersten Funktionsarguments U - Typ des zweiten Funktionsarguments R - Funktionsergebnistyp

Abstrakte Methode


R apply​(T t, U u);

Anwendungsbeispiel in JDK

java.base : java.util.Map<K,​V>

computeIfAbsent

Map.computeIfAbsent


default V computeIfAbsent(K key,
        Function<? super K, ? extends V> mappingFunction) {
    Objects.requireNonNull(mappingFunction);
    V v;
    if ((v = get(key)) == null) {
        V newValue;
        if ((newValue = mappingFunction.apply(key)) != null) {
            put(key, newValue);
            return newValue;
        }
    }

    return v;
}

Stichprobe

Map<String, Integer> map = new HashMap<>();

// (1)
Integer value = map.computeIfAbsent("apple", new Function<String, Integer>(){
	@Override
	public Integer apply(String t) {
		return t.length();
	}
});

// (2)
value = map.computeIfAbsent("banana", s -> {
	return s.length();
});

// (3)
value = map.computeIfAbsent("cherry", s -> s.length());

// (4)Instanzmethodenreferenz
value = map.computeIfAbsent("durian", String::length);

UnaryOperator<T>

Es handelt sich um eine Funktionstyp-Schnittstelle, die in der Super-Schnittstelle Function enthält und sich auf Function spezialisiert hat, die denselben Typ wie der Argumenttyp zurückgibt.

Parameter eingeben: T-Operator-Operanden und Ergebnistypen

Anwendungsbeispiel in JDK

java.base : List<E>

replaceAll

List.replaceAll


default void replaceAll(UnaryOperator<E> operator) {
    Objects.requireNonNull(operator);
    final ListIterator<E> li = this.listIterator();
    while (li.hasNext()) {
        li.set(operator.apply(li.next()));
    }
}

Stichprobe

List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("cherry");

// (1)
list.replaceAll(new UnaryOperator<String>() {
	@Override
	public String apply(String t) {
		return t.toUpperCase();
	}
});

// (2)
list.replaceAll(t -> t.toUpperCase());

// (3)Instanzmethodenreferenz
list.replaceAll(String::toUpperCase);

UnaryOperator.identity

Die UnaryOperator-Schnittstelle verfügt über eine statische Methode namens Identität. (Es ist auch in der Funktionsschnittstelle.)

static <T> UnaryOperator<T> identity() {
    return t -> t;
}

Der Code, den Sie häufig als Beispiel für die Verwendung dieser Methode sehen, konvertiert eine Liste in eine Karte, wie unten gezeigt. Diese Konvertierung legt die ID der Item-Klasse im Schlüssel der Map und die Instanz der Item-Klasse im Wert der Map fest. Die Absicht des Codes wird jedoch durch die Verwendung der Identitätsmethode deutlich, die das Eingabeargument zurückgibt.

List<Item> list = new ArrayList<>();
list.add(new Item(1L, "apple"));
list.add(new Item(2L, "banana"));
list.add(new Item(3L, "cherry"));

//Map<Long, Item> fruitIdMap = list.stream()
//				.collect(Collectors.toMap(Item::getId, i -> i));
// more than better
Map<Long, Item> fruitIdMap = list.stream()
				.collect(Collectors.toMap(Item::getId, UnaryOperator.identity()));

System.out.println(fruitIdMap);
// {1=Item [id=1, name=apple], 2=Item [id=2, name=banana], 3=Item [id=3, name=cherry]}

Prädikat (T zur Booleschen Funktion)

Parameter eingeben: T-Prädikat-Eingabetyp

Abstrakte Methode


boolean test(T t);

BiPredicate Es gibt auch eine funktionale Schnittstelle namens "BiPredicate", die zwei Argumente akzeptiert, die eine Spezialisierung von "Predicate" sind.

Parameter eingeben: T - Typ des ersten Arguments des Prädikats U - Typ des zweiten Prädikatarguments

Abstrakte Methode


boolean test​(T t, U u);

Anwendungsbeispiel in JDK

java.base : java.util.Collection<E>

removeIf

Collection.removeIf


default boolean removeIf(Predicate<? super E> filter) {
    Objects.requireNonNull(filter);
    boolean removed = false;
    final Iterator<E> each = iterator();
    while (each.hasNext()) {
        if (filter.test(each.next())) {
            each.remove();
            removed = true;
        }
    }
    return removed;
}

Stichprobe Dies ist ein Beispiel, bei dem gerade Zahlen aus der Liste entfernt werden. Da removeIf einen Nebeneffekt auf List hat, tritt ein Fehler auf, wenn es sich um eine unveränderliche Liste handelt.

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);

// (1)
list.removeIf(new Predicate<Integer>() {
	@Override
	public boolean test(Integer t) {
		return t % 2 == 0;
	}
});

// (2)
list.removeIf(t -> {
	return t % 2 == 0;
});

// (3)
list.removeIf(t -> t % 2 == 0);

java.base : java.util.Optional<T>

filter

Optional.filter


public Optional<T> filter(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    if (!isPresent()) {
        return this;
    } else {
        return predicate.test(value) ? this : empty();
    }
}

Stichprobe

Optional<Integer> opt = Optional.of(1);

// (1)
Optional<Integer> result = opt.filter(new Predicate<Integer>() {
	@Override
	public boolean test(Integer t) {
		return t % 2 == 0;
	}
});

// (2)
result = opt.filter(t -> {
	return t % 2 == 0;
});

// (3)
result = opt.filter(t -> t % 2 == 0);

result.ifPresentOrElse(System.out::println, () -> {
	System.out.println("it's odd number");
});
// → it's odd number

java.base : java.util.regex.Pattern

asPredicate

Pattern.asPredicate


public Predicate<String> asPredicate() {
    return s -> matcher(s).find();
}

Stichprobe

Pattern pattern = Pattern.compile("^\\d+$");

List<String> list = List.of("123", "abc", "456", "78d");

List<String> result = list.stream()
						.filter(pattern.asPredicate())
						.collect(Collectors.toList());

result.forEach(System.out::println);
// → 123
// → 456

Lieferant (unendliche Funktion zu R)

Parameter eingeben: T - Art des von diesem Lieferanten bereitgestellten Ergebnisses

Abstrakte Methode


T get();

Anwendungsbeispiel in JDK

java.base : java.util.Optional<T>

orElseGet

Optional.orElseGet


public T orElseGet(Supplier<? extends T> supplier) {
    return value != null ? value : supplier.get();
}

Stichprobe

Optional<String> fruit = Optional.ofNullable(null);

// (1)
String result = fruit.orElseGet(new Supplier<String>() {
	@Override
	public String get() {
		return "banana";
	}
});

// (2)
result = fruit.orElseGet(() -> {
	return "banana";
});

// (3)
result = fruit.orElseGet(() -> "banana");

// (4)Methodenreferenz
result = fruit.orElseGet(SupplierDemo::getDefault);

SupplierDemo


public static String getDefault() {
	return "banana";
}

orElseThrow

Optional.orElseThrow


public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
    if (value != null) {
        return value;
    } else {
        throw exceptionSupplier.get();
    }
}

Stichprobe

Optional<String> fruit = Optional.ofNullable(null);

// (1)
String result = fruit.orElseThrow(new Supplier<RuntimeException>() {
	@Override
	public RuntimeException get() {
		return new FruitNotFoundException();
	}
});

// (2)
result = fruit.orElseThrow(() -> {
	return new FruitNotFoundException();
});

// (3)
result = fruit.orElseThrow(() -> new FruitNotFoundException());

// (4)Konstruktorreferenz
result = fruit.orElseThrow(FruitNotFoundException::new);

FruitNotFoundException


class FruitNotFoundException extends RuntimeException {
	public FruitNotFoundException() {
		super();
	}
}

Beispielkonstruktorreferenz

** Beispiel einer Konstruktorreferenz zum Generieren eines Arrays **

List<String> list = List.of("apple", "banana", "cherry", "durian", "elderberry");
// String[] fruits = list.toArray(size -> new String[size]);
// more than better?
String[] fruits = list.toArray(new String[0]);

Der Code zum Konvertieren der obigen Auflistung in ein Array lautet wie folgt, wenn er mit einer Konstruktorreferenz geschrieben wird.

List<String> list = List.of("apple", "banana", "cherry", "durian", "elderberry");
String[] fruits = list.toArray(String[]::new);

java.logging : java.util.logging.Logger

log

Logger.log


public void log(Level level, Supplier<String> msgSupplier) {
    if (!isLoggable(level)) {
        return;
    }
    LogRecord lr = new LogRecord(level, msgSupplier.get());
    doLog(lr);
}

Außer dem Paket java.util.function

Diese Schnittstelle gibt es seit Version 1.8, sie kann jedoch als Zuweisungsziel für Lambda-Ausdrücke oder Methodenreferenzen verwendet werden, da sie die Anforderungen einer funktionalen Schnittstelle erfüllt.

FileFilter

Abstrakte Methode


boolean	accept​(File pathname);

Anwendungsbeispiel in JDK

java.base : File

listFiles

File.listFiles​


public File[] listFiles(FileFilter filter) {
    String ss[] = list();
    if (ss == null) return null;
    ArrayList<File> files = new ArrayList<>();
    for (String s : ss) {
        File f = new File(s, this);
        if ((filter == null) || filter.accept(f))
            files.add(f);
    }
    return files.toArray(new File[files.size()]);
}

Stichprobe

File directory = new File("D:\\temp");
Long limit = 1024L * 1024L * 7L;

File[] overLimitFiles = directory.listFiles(pathname -> pathname.length() > limit);

Runnable

Abstrakte Methode


void run();

Anwendungsbeispiel in JDK

java.base : java.util.Optional<T>

ifPresentOrElse

public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
    if (value != null) {
        action.accept(value);
    } else {
        emptyAction.run();
    }
}

Stichprobe

Optional<String> opt = Optional.ofNullable(null);
opt.ifPresentOrElse(s -> {
	System.out.println(s);
},() -> {
	System.out.println("null");
});

PathMatcher

Abstrakte Methode


boolean	matches​(Path path);

Anwendungsbeispiel in JDK

java.base : java.nio.file.FileSystem

getPathMatcher

FileSystem.getPathMatcher


public abstract PathMatcher getPathMatcher(String syntaxAndPattern);

Stichprobe

Abweichend vom Betreff des Artikels ist der an die Methode "getPathMatcher" übergebene Wert eine Zeichenfolge aus Syntax und Muster, die durch einen Doppelpunkt (:) getrennt ist. Wenn Sie "glob" für die Syntax angeben, können Sie ein einfacheres Muster angeben, das einem regulären Ausdruck für Pattern ähnelt, und wenn Sie "regex" angeben, können Sie einen regulären Ausdruck für Pattern angeben.

FileSystem fileSystem = FileSystems.getDefault();
PathMatcher matcher = fileSystem.getPathMatcher("glob" + ":" + "**/*.java");

Path path = Paths.get("D:", "temp");
try {
	Files.walk(path).filter(matcher::matches).forEach(System.out::println);
} catch (IOException e) {
	throw new UncheckedIOException(e);
}

Comparator<T>

Zusätzlich zu "compare" deklariert diese Schnittstelle eine abstrakte Methode namens "boolean equals (Object obj)", wie unten angegeben.

Wenn die Schnittstelle eine abstrakte Methode deklariert, die eine der öffentlichen Methoden von java.lang.Object überschreibt, wird dies nicht in der Anzahl der abstrakten Methoden in der Schnittstelle berücksichtigt. Der Grund dafür ist, dass jede Implementierung dieser Schnittstelle Implementierungen von java.lang.Object oder anderswo enthält.

Es erfüllt die Voraussetzungen für eine funktionale Schnittstelle.

Abstrakte Methode


int compare(T o1, T o2);

Anwendungsbeispiel in JDK

java.base : Collections

min

Collections.min


@SuppressWarnings({"unchecked", "rawtypes"})
public static <T> T min(Collection<? extends T> coll, Comparator<? super T> comp) {
    if (comp==null)
        return (T)min((Collection) coll);

    Iterator<? extends T> i = coll.iterator();
    T candidate = i.next();

    while (i.hasNext()) {
        T next = i.next();
        if (comp.compare(next, candidate) < 0)
            candidate = next;
    }
    return candidate;
}

Stichprobe

Dies ist ein Beispiel, das die Priorität und die ID der Jobklasse in aufsteigender Reihenfolge sortiert und den kleinsten Job zurückgibt. Beachten Sie, dass die Sammlung eine große Anzahl von Elementen enthält, wie unten angegeben.

Diese Methode durchläuft die gesamte Sammlung, daher dauert die Zeit proportional zur Größe der Sammlung.

List<Job> list = List.of(
	// Id, Priority
	new Job(1L, 3),
	new Job(2L, 3),
	new Job(3L, 2),
	new Job(4L, 1),
	new Job(5L, 1),
	new Job(6L, 2)
);

// (1)
Job minJob = Collections.min(list, new Comparator<Job>() {
	@Override
	public int compare(Job j1, Job j2) {
		if (Objects.equals(j1.getPriority(), j2.getPriority())) {
			return Long.compare(j1.getId(), j2.getId());
		}
		return Integer.compare(j1.getPriority(), j2.getPriority());
	}
});
System.out.println(minJob);
// → Job [id=4, priority=1]

// (2)
minJob = Collections.min(list, Comparator.comparing(Job::getPriority).thenComparing(Job::getId));
System.out.println(minJob);
// → Job [id=4, priority=1]

Callable<V>

Abstrakte Methode


V call() throws Exception;

Anwendungsbeispiel in JDK

java.base : ExecutorService

submit

ExecutorService.submit


<T> Future<T> submit(Callable<T> task);

Stichprobe

ExecutorService service = Executors.newSingleThreadExecutor();

// (1)
Future<LocalDateTime> future = service.submit(new Callable<LocalDateTime>() {
	@Override
	public LocalDateTime call() throws Exception {
		TimeUnit.SECONDS.sleep(5L);
		return LocalDateTime.now();
	}
});

/*
// (2)
Future<LocalDateTime> future = service.submit(() -> {
    TimeUnit.SECONDS.sleep(5L);
    return LocalDateTime.now();
});
*/

LocalDateTime result = future.get();
service.shutdown();

Recommended Posts

Ein Überprüfungshinweis zur Funktionsoberfläche
Einführung der Funktionsschnittstelle
Enum Review Memo
[Java] Funktionsschnittstelle
Java-Standardfunktionstyp-Schnittstelle
Überprüfungshinweise zu Java NIO 2
[Java] Funktionsschnittstelle / Lambda-Ausdruck
Überprüfungshinweise zum Java Collections Framework
Überprüfungsnotizen zu Queue / BlockingQueue / TransferQueue
Schnittstelle
Nachahmung der funktionalen Schnittstelle von Java mit Kotlin