danilo.pianini@unibo.itgianluca.aguzzi@unibo.itangelo.filaseta@unibo.itCompiled on: 2025-12-05 — versione stampabile
public class FirstComparableBasic {
static void main() {
final List<Person> list = new ArrayList<>();
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);
list.sort(new AgeComparator());
System.out.println(list);
list.sort(new AgeComparator().reversed());
System.out.println(list);
list.sort(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 {
static void main() {
final List<Person> list = new ArrayList<>();
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);
list.sort(new AgeComparator());
System.out.println(list);
list.sort(new AgeComparator().reversed());
System.out.println(list);
list.sort(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 {
static void main() {
final List<Person> list = new ArrayList<>();
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);
final Comparator<Person> ageComparator = new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return Integer.compare(o1.getYearOfBirth(), o2.getYearOfBirth());
}
};
list.sort(ageComparator);
System.out.println(list);
list.sort(ageComparator.reversed());
System.out.println(list);
list.sort(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getName().compareTo(o2.getName());
}
});
System.out.println(list);
}
}
package it.unibo.lambdas.intro;
import java.util.ArrayList;
import java.util.List;
public class FirstComparableWithLambdas {
static void main() {
final List<Person> list = new ArrayList<>();
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);
list.sort((Person o1, Person o2) -> Integer.compare(o1.getYearOfBirth(), o2.getYearOfBirth()));
System.out.println(list);
list.sort((o1, o2) -> Integer.compare(o2.getYearOfBirth(), o1.getYearOfBirth()));
System.out.println(list);
list.sort((o1, o2) -> o1.getName().compareTo(o2.getName()));
System.out.println(list);
}
}
void)
interface detta “funzionale”(T1 x1, ..., Tn xn) -> { <body> }(x1, ..., xn) -> { <body> }x -> { <body> }(T1 x1,..,Tn xn) -> <exp>(x1,..,xn) -> <exp>x -> <exp>package it.unibo.lambdas.intro;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class FilteringWithLambdas {
static void main() {
final List<String> list = Arrays.asList("foo", "bar", "foobar!", "AAAAAAA", "!!!");
var l1 = new ArrayList<>(list);
l1.removeIf((String s) -> { return s.length() > 3; }); // Sintassi "completa"
System.out.println(l1); // [foo, bar, !!!]
var l2 = new ArrayList<>(list);
l2.removeIf(s -> { return s.startsWith("f"); }); // Sintassi con tipo dell'argomento inferito
System.out.println(l2); // [bar, AAAAAAA, !!!]
var l3 = new ArrayList<>(list);
l3.removeIf((String s) -> s.contains("!")); // Sintassi con corpo dell'espressione semplificato
System.out.println(l3);
var l4 = new ArrayList<>(list);
l4.removeIf(s -> s.matches("(foo)|(bar)")); // Sintassi più concisa
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)package it.unibo.lambdas.first;
import java.util.Arrays;
import java.util.List;
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); }
static void main() {
final List<String> list = Arrays.asList("a", "bb", "c", "ddd");
final AllLambdas2 objAL = new AllLambdas2();
list.sort((x, y) -> staticMyCompare(x, y));
list.sort(AllLambdas2::staticMyCompare); // same as above
System.out.println(list); // [a, bb, c, ddd]
list.sort((x, y) -> objAL.instanceMyCompare(x, y));
list.sort(objAL::instanceMyCompare); // same as above
System.out.println(list); // [ddd, c, bb, a]
list.sort((x, y) -> x.compareTo(y));
list.sort(String::compareTo); // same as above
System.out.println(list); // [ddd, c, bb, a]
}
}
interface con un singolo metodo astratto
.class per ogni lambdapackage it.unibo.lambdas.first;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class FirstComparable2 {
static void main() {
final List<Person> list = new ArrayList<>();
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
list.sort((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 è:
list.sort(new Comparator<Person>() {
public int compare(Person p1, Person p2) {
return Integer.compare(p2.getYear(), p1.getYear());
}
});
System.out.println(list);
}
}
package it.unibo.lambdas.first;
// Similar to java.util.functions.Predicate<T>
public interface Filter<X> {
boolean applyFilter(X x); // Does element x pass the filter?
}
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
public class FilterUtility {
public static <X> Set<X> filterAll(Collection<X> set, Filter<X> filter) {
final Set<X> newSet = new LinkedHashSet<>();
for (final X x : set) {
if (filter.applyFilter(x)) {
newSet.add(x);
}
}
return newSet;
}
static void main() {
final List<Integer> ls = List.of(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 interfacceinterfaceinterface I { ... default int m(){ ... } }Iterable, Iterator, Collection, Comparator@FunctionalInterfacejava.util.functionjava.util.function vengono fornite varie interfacce “general purpose”apply, accept, test o getjava.util.function, funzioni che lavorano su oggetti generici| Interface | Method signature | Lambda type |
|---|---|---|
Consumer<T> |
void accept(T t) |
(T) -> void |
BiConsumer<T,U> |
void accept(T t, U u) |
(T, U) -> void |
Function<T,R> |
R apply(T t) |
(T) -> R |
BiFunction<T,U,R> |
R apply(T t, U u) |
(T, U) -> R |
UnaryOperator<T> |
T apply(T t) |
(T) -> T |
BinaryOperator<T> |
T apply(T t1, T t2) |
(T, T) -> T |
Predicate<T> |
boolean test(T t) |
(T) -> boolean |
BiPredicate<T,U> |
boolean test(T t, U u) |
(T, U) -> boolean |
Supplier<T> |
T get() |
() -> T |
java.lang.Runnable |
void run() |
() -> void |
java.util.function, funzioni che lavorano su int| Interface | Method signature | Lambda type |
|---|---|---|
IntConsumer |
void accept(int value) |
(int) -> void |
IntFunction<R> |
R apply(int value) |
(int) -> R |
ToIntFunction<T> |
int applyAsInt(T value) |
(T) -> int |
ToIntBiFunction<T,U> |
int applyAsInt(T t, U u) |
(T, U) -> int |
IntSupplier |
int getAsInt() |
() -> int |
IntUnaryOperator |
int applyAsInt(int operand) |
(int) -> int |
IntBinaryOperator |
int applyAsInt(int left, int right) |
(int, int) -> int |
IntPredicate |
boolean test(int value) |
(int) -> boolean |
IntToLongFunction |
long applyAsLong(int value) |
(int) -> long |
IntToDoubleFunction |
double applyAsDouble(int value) |
(int) -> double |
java.util.function, funzioni che lavorano su long| Interface | Method signature | Lambda type |
|---|---|---|
LongConsumer |
void accept(long value) |
(long) -> void |
LongFunction<R> |
R apply(long value) |
(long) -> R |
ToLongFunction<T> |
long applyAsLong(T value) |
(T) -> long |
ToLongBiFunction<T,U> |
long applyAsLong(T t, U u) |
(T, U) -> long |
LongSupplier |
long getAsLong() |
() -> long |
LongUnaryOperator |
long applyAsLong(long operand) |
(long) -> long |
LongBinaryOperator |
long applyAsLong(long left, long right) |
(long, long) -> long |
LongPredicate |
boolean test(long value) |
(long) -> boolean |
LongToIntFunction |
int applyAsInt(long value) |
(long) -> int |
LongToDoubleFunction |
double applyAsDouble(long value) |
(long) -> double |
java.util.function, funzioni che lavorano su double| Interface | Method signature | Lambda type |
|---|---|---|
DoubleConsumer |
void accept(double value) |
(double) -> void |
DoubleFunction<R> |
R apply(double value) |
(double) -> R |
ToDoubleFunction<T> |
double applyAsDouble(T value) |
(T) -> double |
ToDoubleBiFunction<T,U> |
double applyAsDouble(T t, U u) |
(T, U) -> double |
DoubleSupplier |
double getAsDouble() |
() -> double |
DoubleUnaryOperator |
double applyAsDouble(double operand) |
(double) -> double |
DoubleBinaryOperator |
double applyAsDouble(double left, double right) |
(double, double) -> double |
DoublePredicate |
boolean test(double value) |
(double) -> boolean |
DoubleToIntFunction |
int applyAsInt(double value) |
(double) -> int |
DoubleToLongFunction |
long applyAsLong(double value) |
(double) -> long |
Predicatepackage it.unibo.lambdas.first;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
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 LinkedHashSet<>();
for (final X x : set) {
if (filter.test(x)) {
newSet.add(x);
}
}
return newSet;
}
static void main() {
final List<Integer> ls = List.of(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]
}
}
Runnablepackage it.unibo.lambdas.first;
import java.util.List;
public class RunnableUtility {
private static void repeat(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();
}
}
static void main() {
repeat(10, () -> System.out.println("ok"));
batchExecution(
List.of(
() -> System.out.println("a"),
() -> System.out.println("b"),
() -> System.out.println("c"),
() -> System.exit(0)
)
);
}
}
final Function<Integer, Function<Integer, Integer>> multiplier = x -> (y -> x * y);
final Function<Integer, Integer> doubler = multiplier.apply(2);
assertEquals(10, doubler.apply(5));
final 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"));
finalfinal int k = 10;
Predicate<String> p = (s) -> s.length() > k;
p.test("hello"); // false
p.test("hello world"); // true
filterAllStream<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) {..}
}
Mappackage it.unibo.lambdas.first;
import java.util.LinkedHashMap;
import java.util.Map;
public class UseMap {
static void main() {
final Map<Integer, String> map = new LinkedHashMap<>();
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}
}
}
StreamStream rappresenta un flusso sequenziale (anche infinito) di dati omogenei,
usabile una volta solaIterator, ma lo Stream è più dichiarativo,
perché non indica passo-passo come l’informazione viene processata, e quindi è concettualmente più astrattoStream manipola i suoi elementi in modo “lazy” (ritardato):
i dati vengono processati mano a mano che servono,
non sono memorizzati tutti assieme come nelle CollectionList<Persona> con proprietà reddito e città, ottenere la somma dei redditi di tutte le persone di CesenaPerson – equals, hashCode e toString omessipublic final class Person {
private final String name;
private final 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 = city; // null in ingresso indica città assente
this.income = income;
this.jobs = Set.copyOf(Arrays.asList(jobs)); // conversione a set
}
// Getters
public String getName() { return this.name; }
public String getCity() { return this.city; }
public double getIncome() { return this.income; }
public Set<String> getJobs() { return this.jobs; } // Set immutabile, non serve copia
// Hashcode, equals, toString
@Override public int hashCode() { return Objects.hash(name, city, income, jobs); }
@Override public boolean equals(Object obj) {
return this == obj
|| obj instanceof Person person
&& Objects.equals(name, person.name)
&& Objects.equals(city, person.city)
&& Double.compare(income, person.income) == 0
&& Objects.equals(jobs, person.jobs);
}
@Override public String toString() {
return "Person [name=" + name + ", city=" + city + ", income=" + income + ", jobs=" + jobs + "]";
}
}
public class UseStreamsOnPerson {
static void main() {
final List<Person> list = List.of(
new Person("Mario", "Cesena", 20000, "Teacher"),
new Person("Rino", "Forlì", 50000, "Professor"),
new Person("Lino", "Cesena", 110000, "Professor", "Dean"),
new Person("Ugo", "Cesena", 20000, "Secretary"),
new Person("Marco", null, 4000, "Contractor")
);
final double result = list.stream()
.filter(it -> "Cesena".equals(it.getCity())) // Tieni solo i cesenati
.mapToDouble(Person::getIncome) // Prendi il loro reddito
.sum(); // Somma
System.out.println(result);
// alternativa con iteratore: qual è la più leggibile?
double totalIncome = 0; // inizializza somma
for (final Person p: list) { // per ogni persona nella lista
if ("Cesena".equals(p.getCity())) { // se la città è cesena
totalIncome = totalIncome + p.getIncome(); // allora la somma aumenta del suo reddito
}
}
System.out.println(totalIncome);
}
}
Questa è solo una introduzione alle lambda in Java. Nel blocco di approfondimento si trovano ulteriori informazioni su come:
danilo.pianini@unibo.itgianluca.aguzzi@unibo.itangelo.filaseta@unibo.itCompiled on: 2025-12-05 — versione stampabile
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.