gianluca.aguzzi@unibo.it
angelo.filaseta@unibo.it
Questo materiale è ampiamente basato su quello realizzato dai Prof. Mirko Viroli e Roberto Casadei, che ringrazio.
Ogni errore riscontratovi è esclusiva responsabilità degli autori di questo documento.
public class FirstComparableBasic {
public static void main(String[] args) {
final List<Person> list = new ArrayList<Person>();
list.add(new Person("Mario", 1960, true));
list.add(new Person("Gino", 1970, false));
list.add(new Person("Rino", 1951, true));
System.out.println(list);
Collections.sort(list, new AgeComparator());
System.out.println(list);
Collections.sort(list, new AgeComparator().reversed());
System.out.println(list);
Collections.sort(list, new NameComparator());
System.out.println(list);
}
}
class AgeComparator implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
return Integer.compare(o1.getYearOfBirth(), o2.getYearOfBirth());
}
}
class NameComparator implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
return o1.getName().compareTo(o2.getName());
}
}
B
(static nested) è definita all’interno di un’altra classe A
A.B
(secondo regole di visibilità), o direttamente via B
da dentro la classe A
– approfondimentopublic class FirstComparableNested {
public static void main(String[] args) {
final List<Person> list = new ArrayList<Person>();
list.add(new Person("Mario", 1960, true));
list.add(new Person("Gino", 1970, false));
list.add(new Person("Rino", 1951, true));
System.out.println(list);
Collections.sort(list, new AgeComparator());
System.out.println(list);
Collections.sort(list, new AgeComparator().reversed());
System.out.println(list);
Collections.sort(list, new NameComparator());
System.out.println(list);
}
private static class AgeComparator implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
return Integer.compare(o1.getYearOfBirth(), o2.getYearOfBirth());
}
}
private static class NameComparator implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
return o1.getName().compareTo(o2.getName());
}
}
}
public class FirstComparableWithAnonymousClasses {
public static void main(String[] args) {
final List<Person> list = new ArrayList<Person>();
list.add(new Person("Mario", 1960, true));
list.add(new Person("Gino", 1970, false));
list.add(new Person("Rino", 1951, true));
System.out.println(list);
Comparator<Person> ageComparator = new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return Integer.compare(o1.getYearOfBirth(), o2.getYearOfBirth());
}
};
Collections.sort(list, ageComparator);
System.out.println(list);
Collections.sort(list, ageComparator.reversed());
System.out.println(list);
Collections.sort(list, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getName().compareTo(o2.getName());
}
});
System.out.println(list);
}
}
public class FirstComparableWithLambdas {
public static void main(String[] args) {
final List<Person> list = new ArrayList<Person>();
list.add(new Person("Mario", 1960, true));
list.add(new Person("Gino", 1970, false));
list.add(new Person("Rino", 1951, true));
System.out.println(list);
Collections.sort(list,
(Person o1, Person o2) -> Integer.compare(o1.getYearOfBirth(), o2.getYearOfBirth()));
System.out.println(list);
Collections.sort(list,
(o1, o2) -> { return Integer.compare(o2.getYearOfBirth(), o1.getYearOfBirth()); } );
System.out.println(list);
Collections.sort(list, (o1, o2) -> o1.getName().compareTo(o2.getName()));
System.out.println(list);
}
}
void
)
interface
detta “funzionale”delegate
di C#)(T1 x1, ..., Tn xn) -> { <body> }
(x1, ..., xn) -> { <body> }
x -> { <body> }
(T1 x1,..,Tn xn) -> <exp>
(x1,..,xn) -> <exp>
x -> <exp>
Esempi di Lambda
public class FilteringWithLambdas {
public static void main(String[] args) {
final List<String> list = Arrays.asList("foo", "bar", "foobar!", "AAAAAAA", "!!!");
var l1 = new ArrayList<String>(list);
l1.removeIf((String s) -> { return s.length() > 3; });
System.out.println(l1); // [foo, bar, !!!]
var l2 = new ArrayList<String>(list);
l2.removeIf(s -> { return s.startsWith("f"); });
System.out.println(l2); // [bar, AAAAAAA, !!!]
var l3 = new ArrayList<String>(list);
l3.removeIf((String s) -> s.contains("!"));
System.out.println(l3);
var l4 = new ArrayList<String>(list);
java.util.function.Predicate<String> p = s -> s.matches("(foo)|(bar)");
l4.removeIf(p.negate());
System.out.println(l4);
}
}
<class>::<static-method>
(x1, ..., xn) -> <class>.<static-method>(x1, ..., xn)
<class>::<instance-method>
(x1, ..., xn) -> x1.<instance-method>(x2, ..., xn)
<obj>::<method>
(x1, ..., xn) -> <obj>.<method>(x1, ..., xn)
<class>::new
(x1, ..., xn) -> new <class>(x1, ..., xn)
Esempi di Method Reference (casi 1,2,3)
public class AllLambdas2 {
private static int staticMyCompare(final String a, final String b) {
return a.compareTo(b);
}
private int instanceMyCompare(final String a, final String b) {
return b.compareTo(a);
}
public static void main(String[] args) {
final List<String> list = Arrays.asList("a", "bb", "c", "ddd");
final AllLambdas2 objAL = new AllLambdas2();
Collections.sort(list, (x,y) -> staticMyCompare(x,y));
Collections.sort(list, AllLambdas2::staticMyCompare); // same as above
System.out.println(list); // [a, bb, c, ddd]
Collections.sort(list, (x,y) -> objAL.instanceMyCompare(x, y));
Collections.sort(list, objAL::instanceMyCompare); // same as above
System.out.println(list); // [ddd, c, bb, a]
Collections.sort(list, (x,y) -> x.compareTo(y));
Collections.sort(list, String::compareTo); // same as above
System.out.println(list); // [ddd, c, bb, a]
}
}
interface
con un singolo metodo astratto
default
).class
per ogni lambdapublic class FirstComparable2 {
public static void main(String[] args) {
final List<Person> list = new ArrayList<Person>();
list.add(new Person("Mario", 1960, true));
list.add(new Person("Gino", 1970, false));
list.add(new Person("Rino", 1951, true));
System.out.println(list);
// Sorting with a lambda
Collections.sort(list, (p1, p2) -> Integer.compare(p2.getYear(), p1.getYear()));
System.out.println(list);
// Nota che sort richiede un Comparator<Persona>, che ha il solo metodo:
// int compare(Persona p1, Persona p2)
// Quindi il codice equivalente generato da javac è:
Collections.sort(list, new Comparator<Person>() {
public int compare(Person p1, Person p2) {
return Integer.compare(p2.getYear(), p1.getYear());
}
});
System.out.println(list);
}
}
// Similar to java.util.functions.Predicate<T>
public interface Filter<X> {
boolean applyFilter(X x); // Does element x pass the filter?
}
public class FilterUtility {
public static <X> Set<X> filterAll(Collection<X> set, Filter<X> filter) {
final Set<X> newSet = new HashSet<>();
for (final X x : set) {
if (filter.applyFilter(x)) { newSet.add(x); }
}
return newSet;
}
public static void main(String[] args) {
final List<Integer> ls = Arrays.asList(10, 20, 30, 40, 50, 60);
// Nota che il nome del metodo in Filter non è mai menzionato qui
System.out.println(filterAll(ls, x -> x > 20)); // [30,40,50,60]
System.out.println(filterAll(ls, x -> x > 20 && x < 60)); // [30,40,50]
System.out.println(filterAll(ls, x -> x % 20 == 0)); // [20,40,60]
}
}
default
nelle interfacceinterface
interface I { ... default int m(){ ... } }
Iterable
, Iterator
, Collection
, Comparator
@FunctionalInterface
java.util.function
java.util.function
vengono fornite varie interfacce “general purpose”apply
, accept
, test
o get
java.util.function
Consumer<T>
: accept:(T)->void
Function<T,R>
: apply:(T)->R
Predicate<T>
: test:(T)->boolean
Supplier<T>
: get:()->T
UnaryOperator<T>
: apply:(T)->T
BiConsumer<T,U>
: accept:(T,U)->void
BiFunction<T,U,R>
: apply:(T,U)->R
BinaryOperator<T>
: apply:(T,T)->T
BiPredicate<T,U>
: test:(T,U)->boolean
java.lang.Runnable
: run:()->void
BooleanSupplier
: get:()->boolean
IntConsumer
: accept:(int)->void
Predicate
import java.util.*;
import java.util.function.Predicate;
public class FilterUtility2 {
public static <X> Set<X> filterAll(Collection<X> set, Predicate<X> filter) {
final Set<X> newSet = new HashSet<>();
for (final X x : set) {
if (filter.test(x)) {
newSet.add(x);
}
}
return newSet;
}
public static void main(String[] args) {
final List<Integer> ls = Arrays.asList(10, 20, 30, 40, 50, 60);
// Note that the name of the method in Filter is never mentioned here
System.out.println(filterAll(ls, x -> x > 20)); // [30,40,50,60]
System.out.println(filterAll(ls, x -> x > 20 && x < 60)); // [30,40,50]
System.out.println(filterAll(ls, x -> x % 20 == 0)); // [20,40,60]
}
}
Runnable
public class RunnableUtility {
private static void iterate(final int howMany, final Runnable r) {
for (int i = 0; i < howMany; i++) {
r.run();
}
}
private static void batchExecution(final List<Runnable> list) {
for (final Runnable r : list) {
r.run();
}
}
public static void main(String[] args) {
iterate(10, () -> System.out.println("ok"));
final List<Runnable> list = Arrays.asList(
() -> System.out.println("a"),
() -> System.out.println("b"),
() -> System.out.println("c"),
() -> System.exit(0)); // Inferenza su asList automatica!
batchExecution(list);
}
}
import java.util.function.*;
Function<Integer, Function<Integer, Integer>> multiplier = x -> (y -> x * y);
Function<Integer, Integer> doubler = multiplier.apply(2);
assertEquals(10, doubler.apply(5));
BiFunction<Predicate<String>, Predicate<String>, Predicate<String>> and =
(p1, p2) -> (s -> p1.test(s) && p2.test(s));
Predicate<String> p = and.apply(s -> s.length() < 5, s -> s.toUpperCase().equals(s));
assertFalse(p.test("abc"));
assertFalse(p.test("ABCDEFG"));
assertTrue(p.test("ABC"));
filterAll
Stream<T>
e sue manipolazioni, per lavorare su dati sequenziali (collezioni, file, …)java.util.Map
– metodi aggiuntivipublic interface Map<K,V> {
...
default V getOrDefault(Object key, V defaultValue) {..}
default void forEach(BiConsumer<? super K, ? super V> action) {..}
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {..}
default V putIfAbsent(K key, V value) {..}
default boolean remove(Object key, Object value) {..}
default boolean replace(K key, V oldValue, V newValue) {..}
default V replace(K key, V value) {..}
default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {..}
default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {..}
default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {..}
default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {..}
}
Map
public class UseMap {
public static void main(String[] args) {
final Map<Integer, String> map = new HashMap<>();
map.put(10, "a");
map.put(20, "bb");
map.put(30, "ccc");
map.forEach((k, v) -> System.out.println(k + " " + v));
map.replaceAll((k, v) -> v + k); // nuovi valori
System.out.println(map);
// {20=bb20, 10=a10, 30=ccc30}
map.merge(5, ".", String::concat);
map.merge(10, ".", String::concat);
System.out.println(map);
// {20=bb20, 5=., 10=a10., 30=ccc30}
System.out.println(map.getOrDefault(5, "no")); // "."
System.out.println(map.getOrDefault(6, "no")); // "no"
}
}
List<Persona>
con proprietà reddito e città, ottenere la somma dei redditi di tutte le persone di CesenaPerson
– equals
, hashCode
e toString
omessipublic class Person {
private final String name;
private final Optional<String> city;
private final double income;
private final Set<String> jobs;
public Person(String name, String city, double income, String... jobs) {
this.name = Objects.requireNonNull(name);
this.city = Optional.ofNullable(city); // null in ingresso indica città assente
this.income = income;
this.jobs = new HashSet<>(Arrays.asList(jobs)); // conversione a set
}
public String getName() {
return this.name;
}
public Optional<String> getCity() {
return this.city;
}
public double getIncome() {
return this.income;
}
public Set<String> getJobs(){
return Collections.unmodifiableSet(this.jobs); // copia difensiva
}
//.. seguono hashCode, equals e toString
public class UseStreamsOnPerson {
public static void main(String[] args) {
final List<Person> list = new ArrayList<>();
list.add(new Person("Mario","Cesena",20000,"Teacher"));
list.add(new Person("Rino","Forlì",50000,"Professor"));
list.add(new Person("Lino","Cesena",110000,"Professor","Dean"));
list.add(new Person("Ugo","Cesena",20000,"Secretary"));
list.add(new Person("Marco",null,4000,"Contractor"));
final double result = list.stream()
.filter(p->p.getCity().isPresent())
.filter(p->p.getCity().get().equals("Cesena"))
.mapToDouble(Person::getIncome)
.sum();
System.out.println(result);
// alternativa con iteratore: qual è la più leggibile?
double res2 = 0.0;
for (final Person p: list){
if (p.getCity().isPresent() && p.getCity().get().equals("Cesena")){
System.out.println(p);
res2 = res2 + p.getIncome();
}
}
System.out.println(res2);
}
}
Questa è solo una introduzione alle lambda in Java. Nel blocco di approfondimento si trovano ulteriori informazioni su come: