Ceci est une revue de l'interface fonctionnelle introduite dans Java 1.8.
environnement
référence
Dans ce code, la méthode forEach reçoit une interface fonctionnelle de type Consumer. Cette écriture est simplifiée, mais
List<String> list = List.of("a", "b", "c");
list.forEach(System.out::println);
Si vous écrivez Java 1.8 ou une version antérieure, ce sera comme suit.
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);
De plus, si vous écrivez une classe anonyme, ce sera comme suit.
List<String> list = List.of("a", "b", "c");
list.forEach(new Consumer<Object>() {
@Override
public void accept(Object t) {
System.out.println(t);
}
});
Ces styles d'écriture peuvent être simplifiés en utilisant les expressions lambda introduites dans Java 1.8.
// (1)Si vous modifiez le style d'écriture de la classe anonyme en expression lambda, vous pouvez l'écrire comme ceci.
list.forEach((String t) -> {
System.out.println(t);
});
// (2)Le type d'argument est explicite et peut être omis.
list.forEach((t) -> {
System.out.println(t);
});
// (3)S'il n'y a qu'un seul argument, l'argument()Peut être omis.
list.forEach(t -> {
System.out.println(t);
});
// (4)Si le code dans le bloc est une ligne{}Peut être omis. Vous n'avez même pas besoin d'un point-virgule dans votre code.
list.forEach(t -> System.out.println(t));
// (5)Méthode abstraite d'interface fonctionnelle (dans ce cas Consumer).accepter) signature et
//Si les signatures des méthodes à exécuter correspondent, il peut être écrit comme référence de méthode.
list.forEach(System.out::println);
Les interfaces fonctionnelles sont annotées avec @ FunctionalInterface
,
@FunctionalInterface
public interface Consumer<T> {
//...
}
Un type d'annotation d'information utilisé pour indiquer qu'une déclaration de type d'interface est destinée à être une interface fonctionnelle telle que définie dans la spécification du langage Java. Conceptuellement, une interface fonctionnelle n'a qu'une seule méthode abstraite.
Comme indiqué ci-dessus, la condition qui satisfait à la définition d'une interface fonctionnelle est que l'interface n'a qu'une seule méthode abstraite déclarée.
Indépendamment du fait que le type d'annotation FunctionalInterface existe ou non dans la déclaration d'interface, le compilateur traite toute interface répondant à la définition d'une interface fonctionnelle comme une interface fonctionnelle.
De plus, si cette condition est remplie, le compilateur sera traité comme une interface fonctionnelle avec ou sans cette annotation.
Ce package définit une interface fonctionnelle de base. Des exemples typiques sont «Consommateur», «Fonction», «Prédicat» et «Fournisseur», et il y a aussi «IntCounsumer »et« IntFunction »qui sont spécialisés de ceux-ci. Il existe également des interfaces fonctionnelles implémentées à des fins spécifiques en dehors de ce package.
Paramètres de type: Type d'entrée T-Operation
Méthode abstraite
void accept(T t);
BiConsumer Il existe également une interface fonctionnelle appelée «BiConsumer» qui accepte deux arguments, qui est une spécialisation de «Consommateur».
Paramètres de type: T --Type du premier argument de l'opération U --Type du deuxième argument de l'opération
Méthode abstraite
void accept(T t, U u);
java.base : java.util.Iterator<E>
forEach
L'un des moyens les plus courants d'utiliser Consumer est la méthode forEach de l'itérateur.
Iterable.forEach
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
échantillon Depuis Java 1.8, n'écrivez pas comme (1), mais écrivez comme (2) ou (3) avec une expression lambda. Si vous voulez utiliser une ligne comme (2), vous n'avez pas besoin de la mettre dans un bloc et vous pouvez écrire comme (3). De plus, le code peut être simplifié en l'écrivant comme référence de méthode comme dans (4).
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)Référence de la méthode
list.forEach(System.out::println);
Paramètres de type: Type d'entrée T-Function R - Type de résultat de la fonction
Méthode abstraite
R apply(T t);
BiFunction Il existe également une interface fonctionnelle appelée «BiFunction» qui prend deux arguments, qui est une spécialisation de «Function».
Paramètres de type: T --Type du premier argument de la fonction U --Type du deuxième argument de la fonction R --Type de résultat de la fonction
Méthode abstraite
R apply(T t, U u);
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;
}
échantillon
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)Référence de méthode d'instance
value = map.computeIfAbsent("durian", String::length);
UnaryOperator<T>
C'est une interface de type de fonction qui a Function
dans la super interface et se spécialise dans Function qui renvoie le même type que le type d'argument.
Paramètres de type: Opérandes T-Operator et types de résultats
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()));
}
}
échantillon
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)Référence de méthode d'instance
list.replaceAll(String::toUpperCase);
UnaryOperator.identity
L'interface UnaryOperator a une méthode statique appelée identité. (Il se trouve également dans l'interface de fonction.)
static <T> UnaryOperator<T> identity() {
return t -> t;
}
Le code que vous voyez souvent comme exemple d'utilisation de cette méthode convertit une liste en carte comme indiqué ci-dessous. Cette conversion définit l'id de la classe Item dans la clé de la mappe et l'instance de la classe Item dans la valeur de la mappe, mais l'intention du code devient claire en utilisant la méthode d'identité qui renvoie l'argument d'entrée.
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]}
Paramètres de type: Type d'entrée T-Predicate
Méthode abstraite
boolean test(T t);
BiPredicate Il existe également une interface fonctionnelle appelée «BiPredicate» qui prend deux arguments qui sont une spécialisation de «Predicate».
Paramètres de type: T --Type du premier argument du prédicat U --Type du deuxième argument du prédicat
Méthode abstraite
boolean test(T t, U u);
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;
}
échantillon Ceci est un exemple qui supprime les nombres pairs de la liste. Puisque removeIf a un effet secondaire sur la liste, une erreur se produira s'il s'agit d'une liste immuable.
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();
}
}
échantillon
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();
}
échantillon
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
Paramètres de type: T - Type de résultat fourni par ce fournisseur
Méthode abstraite
T get();
java.base : java.util.Optional<T>
orElseGet
Optional.orElseGet
public T orElseGet(Supplier<? extends T> supplier) {
return value != null ? value : supplier.get();
}
échantillon
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)Référence de la méthode
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();
}
}
échantillon
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)Référence constructeur
result = fruit.orElseThrow(FruitNotFoundException::new);
FruitNotFoundException
class FruitNotFoundException extends RuntimeException {
public FruitNotFoundException() {
super();
}
}
** Exemple de référence de constructeur pour générer un tableau **
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]);
Le code pour convertir la collection ci-dessus en tableau est le suivant lorsqu'il est écrit avec une référence de constructeur.
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);
}
Cette interface existe depuis la version 1.8, mais elle peut être utilisée comme destination d'affectation pour les expressions lambda ou les références de méthode car elle répond aux exigences d'une interface fonctionnelle.
Méthode abstraite
boolean accept(File pathname);
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()]);
}
échantillon
File directory = new File("D:\\temp");
Long limit = 1024L * 1024L * 7L;
File[] overLimitFiles = directory.listFiles(pathname -> pathname.length() > limit);
Méthode abstraite
void run();
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();
}
}
échantillon
Optional<String> opt = Optional.ofNullable(null);
opt.ifPresentOrElse(s -> {
System.out.println(s);
},() -> {
System.out.println("null");
});
Méthode abstraite
boolean matches(Path path);
java.base : java.nio.file.FileSystem
getPathMatcher
FileSystem.getPathMatcher
public abstract PathMatcher getPathMatcher(String syntaxAndPattern);
échantillon
S'écartant du sujet de l'article, la valeur passée à la méthode getPathMatcher
est une chaîne de syntaxe et de motif séparés par deux points (:).
Si vous spécifiez "glob" pour la syntaxe, vous pouvez spécifier un modèle plus simple similaire à une expression régulière pour Pattern, et si vous spécifiez "regex", vous pouvez spécifier une expression régulière pour Pattern.
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);
}
En plus de compare
, cette interface déclare une méthode abstraite appelée boolean equals (Object obj)
, comme indiqué ci-dessous.
Si l'interface déclare une méthode abstraite qui remplace l'une des méthodes publiques de java.lang.Object, elle ne sera pas reflétée dans le nombre de méthodes abstraites dans l'interface. La raison en est que toute implémentation de cette interface inclut des implémentations de java.lang.Object ou d'ailleurs.
Il remplit les conditions d'une interface fonctionnelle.
Méthode abstraite
int compare(T o1, T o2);
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;
}
échantillon
Il s'agit d'un exemple qui trie la priorité et l'ID de la classe Job dans l'ordre croissant et renvoie le plus petit Job. Notez que si la collection comporte un grand nombre d'éléments, comme cité ci-dessous, cela sera coûteux.
Cette méthode parcourt toute la collection, elle prend donc un temps proportionnel à la taille de la collection.
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]
Méthode abstraite
V call() throws Exception;
submit
ExecutorService.submit
<T> Future<T> submit(Callable<T> task);
échantillon
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