Lambda表达式演示
/*
* Lambda表达式是函数式编程的风格,是为了给SAM接口的变量和形参赋值的一种语法。
* 目的:减少代码的冗余,增加可读性。
*
* 示例1:
* 开启一个线程,这个线程的任务:打印“hello”
要求用实现Runnable接口的方式来创建多线程
Runnable接口符合SAM特征: public abstract void run();
*/
public class Test01 {
@Test
public void test05(){
//使用Lambda表达式
new Thread(()->System.out.println("hello")).start();
}
@Test
public void test04(){
//匿名内部类,匿名对象
new Thread(new Runnable(){
@Override
public void run() {
System.out.println("hello");
}
}).start();
}
@Test
public void test03(){
//匿名内部类,匿名对象
Thread t = new Thread(new Runnable(){
@Override
public void run() {
System.out.println("hello");
}
});
t.start();
}
@Test
public void test02(){
//匿名内部类,对象有名字
Runnable my = new Runnable(){
@Override
public void run() {
System.out.println("hello");
}
};
Thread t = new Thread(my);
t.start();
}
@Test
public void test01(){
// 非Lambda表达式方式实现,并且有名字的实现类实现
MyRunnable my = new MyRunnable();
Thread t = new Thread(my);
t.start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("hello");
}
}
/*
* 示例2:
* 有一个数组,存储了5个学生对象,这个学生类的自然排序是按照编号排序。但是我希望这个数组是按照姓名排序
*
* Comparator符合SAM接口的特征: public abstract int compare(T t1, T t2)
*/
public class Test02 {
@Test
public void test04(){
Student[] arr = new Student[5];
arr[0] = new Student(1,"zhangsan");
arr[1] = new Student(2,"lisi");
arr[2] = new Student(3,"wangwu");
arr[3] = new Student(4,"zhaoliu");
arr[4] = new Student(5,"qianqi");
Arrays.sort(arr, (o1, o2)-> o1.getName().compareTo(o2.getName()));//按照元素的姓名定制排序
for (Student student : arr) {
System.out.println(student);
}
}
@Test
public void test03(){
Student[] arr = new Student[5];
arr[0] = new Student(1,"zhangsan");
arr[1] = new Student(2,"lisi");
arr[2] = new Student(3,"wangwu");
arr[3] = new Student(4,"zhaoliu");
arr[4] = new Student(5,"qianqi");
Arrays.sort(arr, new Comparator<Student>(){
@Override
public int compare(Student o1, Student o2) {
return o1.getName().compareTo(o2.getName());
}
});//按照元素的姓名定制排序
for (Student student : arr) {
System.out.println(student);
}
}
@Test
public void test02(){
Student[] arr = new Student[5];
arr[0] = new Student(1,"zhangsan");
arr[1] = new Student(2,"lisi");
arr[2] = new Student(3,"wangwu");
arr[3] = new Student(4,"zhaoliu");
arr[4] = new Student(5,"qianqi");
Arrays.sort(arr, new NameComparator());//按照元素的姓名定制排序
for (Student student : arr) {
System.out.println(student);
}
}
@Test
public void test01(){
Student[] arr = new Student[5];
arr[0] = new Student(1,"zhangsan");
arr[1] = new Student(2,"lisi");
arr[2] = new Student(3,"wangwu");
arr[3] = new Student(4,"zhaoliu");
arr[4] = new Student(5,"qianqi");
Arrays.sort(arr);//按照元素的自然顺序排列
for (Student student : arr) {
System.out.println(student);
}
}
}
class NameComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.getName().compareTo(o2.getName());
}
}
class Student implements Comparable<Student>{
private int id;
private String name;
public Student(int id, String name) {
super();
this.id = id;
this.name = name;
}
public Student() {
super();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + "]";
}
@Override
public int compareTo(Student o) {
return this.id - o.id;
}
}
Java8新增的函数式接口介绍
/*
* 函数式接口:SAM接口
* Single Abstract Method,即该接口中只有一个抽象方法需要实现,当然该接口可以包含其他非抽象方法(默认方法和静态方法,或者是从Object继承的方法)。
*
*
* 回忆之前学过的接口:
* (1)Runnable
* (2)Comparable
* (3)Comparator
* (4)Iterable
* (5)Iterator
* (6)Collection,Set,List,Map,Queue,Deque....
* (7)Serializable
* (8)Externalizable
* (9)FileFilter
* (10)InvocationHandler
* ....
*
* 哪些是符号SAM特征的呢?
* (1)Runnable public void run()
* (2)Comparable public int compareTo(T t)
* (3)Comparator public int compare(T t1, T t2)
* (4)Iterable public Iterator iterator()
* (5)FileFilter public boolean accept(File pathname);
* (6)InvocationHandler public Object invoke(Object proxy, Method method, Object[] args)
*
* 按照语法来说,只要符号SAM特征的接口,都可以使用Lambda表达式。
* 但是Java建议只针对标记了@FunctionalInterface这注解的SAM接口使用Lambda表达式
*
* 上面哪些标记了@FunctionalInterface注解:
* (1)Runnable public void run()
* (3)Comparator public int compare(T t1, T t2)
* (5)FileFilter public boolean accept(File pathname);
*
* 如果没有标记@FunctionalInterface注解的,说明它考虑了以后可能增加抽象方法。目前使用没问题,就是以后可能有风险。
*
* Java8增加了大量的函数式接口给我们使用,而且基本上能满足常规的所有情况。
* 这些函数式接口集中在java.util.function包中。
* 四大类:
* (1)消费型接口
* 它的抽象方法有一个特征: 有参无返回值 例如:void accept(T t)
* (2)供给型接口
* 它的抽象方法有一个特征: 无参有返回值 例如:T get()
* (3)判断型接口
* 它的抽象方法有一个特征: 有参有返回值,但是返回值类型是boolean 例如:boolean test(T t)
* (4)功能型接口
* 它的抽象方法有一个特征: 有参有返回值 例如: R apply(T t)
*/
public class Test02 {
}
/*
* 这些函数式接口集中在java.util.function包中。
* 四大类:
* (1)消费型接口
* 它的抽象方法有一个特征: 有参无返回值 例如:void accept(T t)
* (2)供给型接口
* 它的抽象方法有一个特征: 无参有返回值 例如:T get()
* (3)判断型接口
* 它的抽象方法有一个特征: 有参有返回值,但是返回值类型是boolean 例如:boolean test(T t)
* (4)功能型接口
* 它的抽象方法有一个特征: 有参有返回值 例如: R apply(T t)
*
* 一、消费型接口
* 经典代表:
* Consumer<T> 抽象方法 void accept(T t)
*
* 延伸代表:各种变形们
(1)BiConsumer<T,U> void accept(T t, U u) 接收两个对象用于完成功能
(2)
DoubleConsumer void accept(double value) 接收一个double值
IntConsumer void accept(int value) 接收一个int值
LongConsumer void accept(long value) 接收一个long值
(3)
ObjDoubleConsumer<T> void accept(T t, double value) 接收一个对象和一个double值
ObjIntConsumer<T> void accept(T t, int value) 接收一个对象和一个int值
ObjLongConsumer<T> void accept(T t, long value) 接收一个对象和一个long值
*
*
* 总结:(1)消费型接口,都以“Consumer”单词为结尾
* (2)Bi开头,传两个参数 Binary(二元的)
* (3)xxConsumer,前面的xx代表形参类型
*
* 二、供给型接口
* 经典代表:
* Supplier<T> 抽象方法 T get()
*
* 延伸的代表们:
*(1)BooleanSupplier boolean getAsBoolean() 返回一个boolean值
*(2)DoubleSupplier double getAsDouble() 返回一个double值
*(3)IntSupplier int getAsInt() 返回一个int值
*(4)LongSupplier long getAsLong() 返回一个long值
*
* 总结:(1)供给型接口以“Supplier”单词结尾
* (2)xxSupplier说明返回xx类型的结果
* (3)供给型接口的抽象方法都是无参的
*
*
* 三、判断型接口
* 经典代表:
* Predicate<T> 抽象方法 boolean test(T t)
*
* 延伸代表们:
(1)BiPredicate<T,U> boolean test(T t, U u) 接收两个对象
(2)DoublePredicate boolean test(double value) 接收一个double值
(3)IntPredicate boolean test(int value) 接收一个int值
(4)LongPredicate boolean test(long value) 接收一个long值
*
* 总结:(1)判断型接口以“Predicate”结尾
* (2)判断型接口抽象方法的返回值类型是固定的,是boolean
* (3)xxPredicate,说明形参是xx类型的
*
* 四、功能型接口
* 经典代表:
* Function<T,R> 抽象方法 R apply(T t)
*
* 延伸代表:
* (1)UnaryOperator<T> T apply(T t) 接收一个T类型对象,返回一个T类型对象结果
* (2)
* DoubleFunction<R> R apply(double value) 接收一个double值,返回一个R类型对象
IntFunction<R> R apply(int value) 接收一个int值,返回一个R类型对象
LongFunction<R> R apply(long value) 接收一个long值,返回一个R类型对象
* (3)
* ToDoubleFunction<T> double applyAsDouble(T value) 接收一个T类型对象,返回一个double
ToIntFunction<T> int applyAsInt(T value) 接收一个T类型对象,返回一个int
ToLongFunction<T> long applyAsLong(T value) 接收一个T类型对象,返回一个long
* (4)
DoubleToIntFunction int applyAsInt(double value) 接收一个double值,返回一个int结果
DoubleToLongFunction long applyAsLong(double value) 接收一个double值,返回一个long结果
IntToDoubleFunction double applyAsDouble(int value) 接收一个int值,返回一个double结果
IntToLongFunction long applyAsLong(int value) 接收一个int值,返回一个long结果
LongToDoubleFunction double applyAsDouble(long value) 接收一个long值,返回一个double结果
LongToIntFunction int applyAsInt(long value) 接收一个long值,返回一个int结果
* (5)
DoubleUnaryOperator double applyAsDouble(double operand) 接收一个double值,返回一个double
IntUnaryOperator int applyAsInt(int operand) 接收一个int值,返回一个int结果
LongUnaryOperator long applyAsLong(long operand) 接收一个long值,返回一个long结果
* (6)BiFunction<T,U,R> R apply(T t, U u) 接收一个T类型和一个U类型对象,返回一个R类型对象结果
* (7)BinaryOperator<T> T apply(T t, T u) 接收两个T类型对象,返回一个T类型对象结果
(8) ToDoubleBiFunction<T,U> double applyAsDouble(T t, U u) 接收一个T类型和一个U类型对象,返回一个double
ToIntBiFunction<T,U> int applyAsInt(T t, U u) 接收一个T类型和一个U类型对象,返回一个int
ToLongBiFunction<T,U> long applyAsLong(T t, U u) 接收一个T类型和一个U类型对象,返回一个long
(9) DoubleBinaryOperator double applyAsDouble(double left, double right) 接收两个double值,返回一个double结果
IntBinaryOperator int applyAsInt(int left, int right) 接收两个int值,返回一个int结果
LongBinaryOperator long applyAsLong(long left, long right) 接收两个long值,返回一个long结果
*
*
* 总结:(1)以Unary开头的,表示一元的,泛型的类型只有一个,形参和返回值都是同一种类型
* (2)xxFunction,说明形参的类型是xx类型的
* (3)toXXFunction,说明返回值类型是xx类型
* (4)xxToyyFunction,说明形参的类型是xx类型的,返回值类型是yy类型
* (5)xxUnary开头的,表示一元,形参类型和返回值类型都是xx
* (6)Bi开头,表示二元,形参类型是2个
* (7)BinaryOperator,既是Bi开头表示两个形参,又是Operator结尾,表示形参和返回值类型是一样
* (8)toXXBi开头的,表返回值类型是xx,并且形参是两个
* (9)xxBinaryOperator,表示两个形参,又是Operator结尾,表示形参和返回值类型是一样
*/
public class Test03 {
}
Lambda表达式的语法
/*
* Lambda表达式是给函数式接口(SAM接口)的变量或形参赋值的表达式。
* Lambda表达式替代了原来使用匿名内部类的对象给函数式接口(SAM接口)的变量或形参赋值的形式。
*
* 匿名内部类:实现了这个接口,重写了接口的抽象方法,同时创建了对象。
* Lambda表达式也要实现这个接口,重写接口的抽象方法,隐含的创建了对象。
* 既然Lambda表达式是SAM接口的变量或形参赋值的,那么实现接口这部分语法可以省略,SAM接口的抽象方法只有一个,因此方法签名可以省略一部分。
* 例如:修饰符,返回值类型、方法名可以省略,但是其中的形参列表不能省略,因此这里表表现形参类型和形参名,这个在方法体的实现中要使用。
*
*
* Lambda表达式语法格式:
* (形参列表) -> {Lambda体}
*
* 说明:(1)(形参列表)就是SAM接口的抽象方法的形参列表
* (2){Lambda体}就是实现SAM接口的抽象方法的方法体
* (3)->称为Lambda操作符,由“减号”和“大于号”构成,中间不能有空格
*
* 优化:
* (1)如果{Lambda体}只有一句语句,可以省略{}以及{}中的语句的结束;,如果{}没有省略的话,;就不能省略
* (2)如果(形参列表)中形参的类型是已知的,获取可以推断,那么数据类型可以省略
* (3)如果(形参列表)只有一个形参,并且数据类型也已经省略了,那么这个()可以省略了,如果数据类型没有省略,()不能省略
* (4)如果{Lambda体}只有一句语句,并且是一个return语句,那么可以省略{}以及{}中的语句的结束;还有return。
* (5)如果没有形参,()不能省略
*/
public class Test03Lambda {
@Test
public void test01(){
//使用Lambda表达式给Runnable接口的形参赋值,这个线程要打印“尚硅谷"
//构造器:new Thread(Runnable target)
//现在要用Lambda表达式给Runnable类型的target形参赋值
/*
* 写好一个Lambda表达式关注两个事情:
* (1)抽象方法长什么样 -->形参列表怎么写
* public void run()
* (2)抽象方法怎么实现,即这个抽象方法要做什么事情 -->Lambda体
*/
//new Thread(() -> {System.out.println("尚硅谷");}).start();
//优化
new Thread(() -> System.out.println("尚硅谷")).start();
}
@Test
public void test02(){
/*
* 在Java8版本,给Iterable<T>这个接口增加一个默认方法
* default void forEach(Consumer<? super T> action) 这个方法可以遍历容器中的元素,做xx事情
*
* Collection<E>接口继承了Iterable<T>接口,说明Collection系列的集合都有这个方法,例如:ArrayList
*
*/
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
list.add("atguigu");
/*
* forEach方法的形参是Consumer类型,它是消费型接口的类型,是SAM接口,就可以使用Lambda表达式赋值
* (1)接口的抽象方法
* void accept(T t) 这里方法名不重要,只看返回值类型和形参列表
* (2)要如何实现这个抽象方法,例如:我这里要打印元素
*/
// list.forEach((String str) -> {System.out.println(str);});
// 优化
// list.forEach((String str) -> System.out.println(str));
//再优化
list.forEach((str) -> System.out.println(str));
//再优化
list.forEach(str -> System.out.println(str));
}
@Test
public void test03(){
//从集合中删除字符个数超过6个的
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
list.add("atguigu");
/*
* Java8中Collection接口增加了一个方法
* default boolean removeIf(Predicate<? super E> filter)
* 这个方法的形参是Predicate判断型接口,是SAM接口,可以使用Lambda表达式赋值
*
* Predicate<T>判断型接口
* (1)抽象方法
* boolean test(T t)
* (2)如何实现抽象方法
* 这里判断条件是 字符个数>6
*/
/* list.removeIf((String str) -> {
if(str.length()>6){
return true;
}else{
return false;
}
});*/
//代码优化
list.removeIf((String str) -> {return str.length()>6;});
//再优化
list.removeIf(str -> str.length()>6);
}
}
/*
* Supplier和Consumer
*/
public class Test04Lambda2 {
@Test
public void test01(){
/*
* Java8中,有一个新的类:Stream类型,它代表一个数据加工流
* java.util.stream.Stream<T>接口
* static <T> Stream<T> generate(Supplier<T> s)
*
* 这个静态方法的形参:Supplier<T>供给型接口的类型,SAM接口,可以使用Lambda表达式赋值
* 接口的静态方法通过“接口名.静态方法”调用
*
* Supplier<T>接口
* (1)抽象方法 T get()
* (2)抽象方法如何实现
* 例如:在抽象方法中实现,随机产生一个小数[0,1),返回值类型double
*/
// Stream<Double> stream = Stream.generate(() -> {return Math.random();});
//优化
Stream<Double> stream = Stream.generate(() -> Math.random());
/*
* 为了看效果,我再调用Stream<T>接口的void forEach(Consumer<? super T> action)
* 这个方法形参是Consumer<T>消费型接口类型,SAM接口,也可以使用Lambda表达式赋值
*
* Consumer<T>消费型接口:
* (1)抽象方法
* void accept(T t)
* (2)如何实现抽象方法
* 例如:打印流中的元素
*
*/
// stream.forEach((Double num) -> {System.out.println(num);});
//优化
stream.forEach(num -> System.out.println(num));
}
}
/*
* JDK1.8时Map接口增加了一个方法:
* default void replaceAll(BiFunction<? super K,? super V,? extends V> function)
*/
public class Test05Lambda3 {
@Test
public void test01(){
HashMap<String,Employee> map = new HashMap<>();
map.put("张三", new Employee("张三", 8000));
map.put("李四", new Employee("李四", 9000));
map.put("王五", new Employee("王五", 12000));
map.put("赵六", new Employee("赵六", 11000));
//把原来薪资低于10000元的,修改为薪资为10000元,其他的不变
/*
* replaceAll(BiFunction<? super K,? super V,? extends V> function)
*
* replaceAll的形参是BiFunction<T,U,R>接口类型,SAM接口,可以使用Lambda表达式赋值
*
* BiFunction<T,U,R>接口:
* (1)抽象方法
* R apply(T t, U u)
* (2)如何实现抽象方法
* 例如:把原来value对象的薪资<10000元的替换(覆盖)为薪资是10000元
*
* 如果抽象方法有返回值,那么实现抽象方法时,需要return语句。
* 这里看返回值类型是V类型,是value的类型
*/
map.replaceAll((String key,Employee emp) -> {
if(emp.getSalary()<10000){
emp.setSalary(10000);
}
return emp;
});
//优化
map.replaceAll((key,emp) -> {
if(emp.getSalary()<10000){
emp.setSalary(10000);
}
return emp;
});
}
}
class Employee {
private String name;
private double salary;
public Employee(String name, double salary) {
super();
this.name = name;
this.salary = salary;
}
public Employee() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Employee [name=" + name + ", salary=" + salary + "]";
}
}
自定义函数式接口及Lambda表达式的代码怎么运行的
public class TestConsumer {
@Test
public void test01(){
List<Integer> list = Arrays.asList(1,2,3,4,5);
/*
* default void forEach(Consumer<? super T> action)
* 形参的类型是Consumer接口,可以使用Lambda表达式赋值
* Consumer<T> void accept(T t)
*
*/
list.forEach(num -> System.out.println(num));
}
/*
*default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
*/
}
方法引用和构造器引用
/*
* lambda表达式是用来简化匿名内部类的一种函数式编程的语法。
* 只有SAM接口才能使用lambda表达式
* 方法引用和构造器引用是用来简化lambda表达式。
* 只有当lambda表达式满足一些特殊情况时,才能使用方法引用和构造器引用。
*
* 条件:
* (1)当Lambda体的实现是通过调用一个现有的方法来完成功能时
* (2)要求函数式接口的抽象方法返回值类型与该方法的返回值类型要对应。
* (3)要求函数式接口的抽象方法的形参列表与该方法的形参列表对应
* 或者
* 要求函数式接口的抽象方法的形参列表的第一个形参是作为调用方法的对象,剩余的形参正好是给这个方法的实参列表
*
* 方法引用的语法格式:
* (1)对象::实例方法名
* (2)类名::静态方法名
* (3)类名::实例方法名
*
* 条件:
* (1)当Lambda体的实现是通过创建一个对象来实现的
* (2)要求函数式接口的抽象方法返回值类型与该对象的类型一致
* (3)要求函数式接口的抽象方法的形参列表与该对象创建时的构造器的形参列表对应
*
* 构造器引用语法格式:
* (1)类名::new
* (2)数组类型::new
*/
public class TestFunctionReference {
@Test
public void test06(){
new Thread(() -> System.out.println("hello")).start();//不能使用方法引用来优化
}
@Test
public void test05(){
//调用createArray这个方法,创建一个2的n次方长度的数组
/*
* public <R> R[] createArray(Function<Integer,R[]> fun,int length)
* 第一个参数:Function<Integer,R[]>接口 ,是SAM接口,可以赋值为Lambda表达式
* Function<T,R>接口的抽象方法: R apply(T t)
*
* R[] apply(Integer i)
* 如何实现,创建一个长度为i的R类型的数组
*
*/
// String[] arr = createArray((Integer i) -> new String[i], 10);
//优化为
String[] arr = createArray(String[]::new, 10);
System.out.println(Arrays.toString(arr));
System.out.println(arr.length);
}
/*
* 模仿HashMap的源码实现的一个方法,功能是,可以创建一个2的n次方长度的数组
* 例如:这个length是你希望的数组的长度,但是它可能不是2的n次方,那么我会对length进行处理,纠正为一个2的n次方长度,并且创建这个数组
*/
public <R> R[] createArray(Function<Integer,R[]> fun,int length){
int n = length - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
length = n < 0 ? 1 : n + 1;
return fun.apply(length);
}
@Test
public void test04(){
//lambda表达式也可以用于给SAM接口的变量赋值
/*
* Supplier<T> 接口的抽象方法 T get()
* 如何实现这个抽象方法,例如:这里要new一个String的空对象
*/
// Supplier<String> s = () -> new String();
//就可以使用构造器引用,来创建对象
Supplier<String> s = String::new;
}
@Test
public void test03(){
String[] arr = {"hello","Hi","world","chailinyan","atguigu"};
//不区分大小写的排序
/*
* public static <T> void sort(T[] a, Comparator<? super T> c)
* 第二个形参:Comparator接口,SAM接口,赋值为Lambda表达式
*
* Comparator<String>接口的抽象方法:
* int compare(String s1, String s2)
* 如何实现?不区分大小写比较字符串
*/
// Arrays.sort(arr, (s1,s2) -> s1.compareToIgnoreCase(s2));
/*
* Comparator<String>接口 int compare(String s1, String s2)
* String类的 int compareToIgnoreCase(String str) 此处,s1正好是调用compareToIgnoreCase的对象,s2是给它的参数
*/
//使用方法引用优化
Arrays.sort(arr, String::compareToIgnoreCase);
System.out.println(Arrays.toString(arr));
}
@Test
public void test02(){
/*
* Stream的
* public static<T> Stream<T> generate(Supplier<T> s)
* 形参是Supplier接口类型,赋值为Lambda表达式
*
* (1)抽象方法 T get()
* (2)如何实现, 用Math.random()随机产生一个[0,1)范围的数
*/
// Stream<Double> stream = Stream.generate(() -> Math.random());
/*
* Supplier接口的抽象方法 T get() Double get()
* 调用的是 double Math.random()
*/
//使用方法引用
Stream<Double> stream = Stream.generate(Math::random);
stream.forEach(System.out::println);
}
@Test
public void test01(){
List<Integer> list = Arrays.asList(1,2,3,4,5);
/*
* default void forEach(Consumer<? super T> action)
* 形参的类型是Consumer接口,可以使用Lambda表达式赋值
* Consumer<T> void accept(T t)
*
*/
//list.forEach(num -> System.out.println(num));
/*
* (1)这里的lambda表达式是通过调用一个现有的方法来完成的,是out对象的println()方法来完成
* (2)Consumer<T> public void accept(T t)
* PrintStream类的out对象 public void println(Object x)
*/
list.forEach(System.out::println);
}
}