zoukankan      html  css  js  c++  java
  • 0028 Java学习笔记-面向对象-Lambda表达式

    匿名内部类与Lambda表达式示例

    下面代码来源于:0027 Java学习笔记-面向对象-(非静态、静态、局部、匿名)内部类

    package testpack;
    public class Test1{  
        public static void main(String[] args) { 
        	ProcessArray pa=new ProcessArray();
        	int[] target={5,35,-2,35,-21};
        	pa.process(target, new Command(){        //匿名内部类实现一个接口Command,不能传入参数
        		public void process(int[] target){
        			int sum=0;
        			for (int tmp:target) {
        				sum+=tmp;
        			}
        			System.out.println("数组元素总和: "+sum);
        		}
        	});
        }
    }
    interface Command{
    	void process(int[] target);
    }
    class ProcessArray{
    	public void process(int[] target,Command cmd){
    	cmd.process(target);	
    	}
    }
    
    • 下面用Lambda表达式改写
    package testpack;
    public class Test1{  
        public static void main(String[] args) { 
        	ProcessArray pa=new ProcessArray();
        	int[] target={5,35,-2,35,-21};
        	pa.process(target, (int[] array)->{   //改写从这开始。Lambda基本结构:()->{}
        		int sum=0;                        
        		for (int tmp:array){
        			sum+=tmp;
        		}
        		System.out.println("数组元素总和: "+sum);
        	});                                    //改写结束
        }
    }
    interface Command{
    	void process(int[] target);
    }
    class ProcessArray{
    	public void process(int[] target,Command cmd){
    	cmd.process(target);	
    	}
    }
    
    • Lambda表达式的基本结构
      • 形参列表:()圆括号部分
        • 形参列表位于圆括号中
        • 形参类型允许省略:上面代码中省略"int[]"也是可以的
        • 如果无参数,那就只写个()
        • 如果只有一个参数,可以省略圆括号
      • 箭头:->
      • 代码块:{}花括号部分
        • 代码块部分放在花括号中
        • 如果只有一条语句,那么可以省略花括号
        • Lambda只有一条return语句,可以省略return关键字,只有一条语句的话,就自动返回该语句的值
    • Lambda表达式示例:来源于《疯狂Java讲义第三部》.《LambdaQs.java》
    package testpack;
    public class Test1{  
        public static void main(String[] args) { 
        	LambdaQs lq=new LambdaQs();
        	lq.eat(()->System.out.println("苹果味道不错!"));   //实际上是创建了Eatable类型的匿名对象,重写了其taste()方法
        	lq.drive(weather->{                               //实际上是创建了Flyable类型的匿名对象,重写了其fly(String weather)方法
        		System.out.println("今天天气是: "+weather);
        		System.out.println("直升机飞行平稳!");
        	});
        	lq.test((a,b)->a+b);                              //创建了Addable类型的对象,重写了add(int a,int b)方法
        }
    }
    interface Eatable{
    	void taste();
    }
    interface Flyable{
    	void fly(String weather);
    }
    interface Addable{
    	int add(int a,int b);
    }
    class LambdaQs{
    	public void eat(Eatable e){
    		System.out.println(e);
    		e.taste();
    	}
    	public void drive(Flyable f){
    		System.out.println("我正在驾驶:"+f);
    		f.fly("碧空如洗的晴日");
    	}
    	public void test(Addable add){
    		System.out.println("5+3= "+add.add(5, 3));
    	}
    }
    

    函数式接口与Lambda表达式

    • 函数式接口:只包含一个抽象方法的接口,可以包含多个默认方法、类方法,但只能包含一个抽象方法
    • 创建函数式接口的对象,可以通过匿名内部类和Lambda表达式
    • 函数式接口:java.lang.Runnable、java.awt.event.ActionListener
    • @FunctionalInterface 注解用于告知编译器执行更严格的检查,该接口必须是函数式接口,否则报错
    • Lambda表达式的结果就是一个对象,可以将其赋值给一个函数式接口类型的变量,只要二者的参数列表匹配,示例:
    package testpack;
    public class Test1{  
        public static void main(String[] args) { 
        	Runnable r=()->{                            //Runnable是一个函数式接口,只有一个无参数的抽象方法
        		System.out.println("lambda表达式的实现了一个无参的方法,可以赋值给Runnable类型变量");
        	};
        	r.run();
        }
    }
    
    • Lambda表达式只能为函数式接口创建对象,即只包含一个抽象方法的接口,只能实现一个方法
    • Lambda表达式的目标类型必须是明确的函数式接口,但具体是哪个类型并不能确定,只要那个接口的抽象方法的参数列表跟Lambda表达式匹配即可,见示例
    package testpack;
    public class Test1{  
        public static void main(String[] args) { 
        	Runnable r=()->System.out.println("同一个Lambda表达式可以赋值给Runnable和A,只要他们抽象方法的参数列表相匹配");
        	r.run();
        	A a=()->System.out.println("同一个Lambda表达式可以赋值给Runnable和A,只要他们抽象方法的参数列表相匹配");
        	a.a();
        }
    }
    interface A{
    	void a();
    }
    
    • 确保正确的使用Lambda表达式
      • 将其赋值给一个函数式接口类型的变量,当然参数列表得匹配
      • 将其作为一个函数式接口类型的参数传给某个方法
      • 使用函数式接口进行强制类型转换
    • 函数式接口有很多,比如下面这些:
      • java.util.function中的函数式接口
        • ...Function:一般包含一个apply()方法,对参数进行处理,然后返回一个值
        • ...Consumer:包含一个accept()方法,与上面方法类似,只是不返回值
        • ...Predicate:包含test()方法,对参数进行判断,返回一个boolean值
        • ...Supplier:包含getAs...()方法,不需要参数,返回一个数据
        • ...Operator:
      • java.util.Comparator

    Lambda表达式、方法引用、构造器引用

    • 如果Lambda表达式的代码块只有一条代码,则还有更加简洁的写法
      • 引用类方法:
        • 类名::类方法
        • 被实现方法的全部参数传给该方法作为参数
        • (a,b,...)->类名.类方法(a,b,...)
      • 引用特定对象的实例方法:
        • 特定对象::实例方法
        • 被实现方法的全部参数传给该方法作为参数
        • (a,b,...)->特定对象.实例方法(a,b,...)
      • 引用某类对象的实例方法:
        • 类名::实例方法
        • 被实现方法的第一个参数作为调用者,后面的参数全部传给该方法作为参数
        • (a,b,c,...)->a.实例方法(b,c,...)
      • 引用构造器:
        • 类名::new;
        • 被实现方法的全部参数传给该构造器作为参数
        • (a,b,...)->new 类名(a,b,...)
    • 见示例:
    package testpack;
    
    import javax.swing.JFrame;
    
    public class Test1{  
        public static void main(String[] args) { 
    
        	Converter c1=(String from)->{return Integer.valueOf(from);};           //Lambda表达式
        	Converter c2=Integer::valueOf;                                         //引用类方法
        	System.out.println("c1: "+c1.convert("199")+"  c2: "+c2.convert("199"));
        	
        	Converter c3=(String from)->{return "ABCDEFGHIJKLMN".indexOf(from);}; //Lambda表达式
        	Converter c4="ABCDEFGHIJKLMN"::indexOf;                               //引用特定对象的实例方法
        	System.out.print(c3.convert("EFG"));
        	System.out.println("     "+c3.convert("EFG"));
        	
        	Sub s1=(String str,int a,int b)->{return str.substring(a,b);};        //Lambda表达式
        	Sub s2=String::substring;                                             //引用某类对象的实例方法
        	System.out.println(s1.sub("ABCDEFGHIJKLMN", 3, 9));
        	System.out.println(s1.sub("ABCDEFGHIJKLMN", 3, 9));
        	
        	Frame f1=(String title)->{return new JFrame(title);};                 //Lambda表达式
        	JFrame jf1=f1.win("我的窗口");
        	Frame f2=JFrame::new;                                                 //引用构造方法
        	JFrame jf2=f2.win("我的窗口");
        	System.out.println(jf1);
        	System.out.println(jf2);
        }
    }
    interface Converter{
    	Integer convert(String from);
    }
    interface Sub{
    	String sub(String str,int a,int b);
    }
    interface Frame{
    	JFrame win(String title);
    }
    

    Lambda表达式与匿名内部类

    • Lambda表达式可以在一定程度上看作是匿名内部类的子集,当匿名内部类实现的接口只有一个抽象方法(也就是函数式接口)时,可以用lambda
    • 相同之处:
      • 二者都可以直接访问外部类的实例变量和类变量,访问的局部变量都默认被final修饰
      • 二者创建的对象,都可以调用从接口中继承的默认方法
      • 见示例:
    package testpack;
    public class Test1{  
        public static void main(String[] args) { 
        	LambdaAndInner li=new LambdaAndInner();
        	li.test();
        }
    }
    class LambdaAndInner{
    	private int age=13;
    	private static String name="ABCDE";
    	public void test(){                    
    		String book="Java编程思想";
    		Display dis=new Display(){      //匿名内部类
    			public void display(){
    				System.out.println("匿名内部类对象可以直接访问,局部变量book:"+book);
    				System.out.println("匿名内部类对象可以直接访问,外部类的实例变量age:"+age);
    				System.out.println("匿名内部类对象可以直接访问,外部类的类变量name:"+name);
    				show();                 //在匿名内部类实现抽象方法的方法体内部调用接口默认方法
    			}
    		};
    		dis.display();
    		dis.show();                     //匿名内部类的对象调用接口的默认方法
    	}
    }
    interface Display{
    	void display();
    	default void show(){
    		System.out.println("这是接口的默认方法");
    	}
    }
    
    package testpack;
    public class Test1{  
        public static void main(String[] args) { 
        	LambdaAndInner li=new LambdaAndInner();
        	li.test();
        }
    }
    class LambdaAndInner{
    	private int age=13;
    	private static String name="ABCDE";
    	public void test(){
    		String book="Java编程思想";
    		Display dis=()->{               //Lambda表达式
    			System.out.println("Lambda表达式对象,可以直接访问局部变量book:"+book);
    			System.out.println("Lambda表达式对象,可以直接访问外部类的实例变量age:"+age);
    			System.out.println("Lambda表达式对象,可以直接访问外部类的类变量name:"+name);
    			show();                     //Lambda表达式代码块内部调用接口默认方法,编译出错
    		};
    		dis.display();
    		dis.show();                    //Lambda表达式的对象调用接口的默认方法
    	}
    }
    interface Display{
    	void display();
    	default void show(){
    		System.out.println("这是接口的默认方法");
    	}
    }
    
    • 区别:
      • 匿名内部类可以为任何接口创建实例,不管有几个抽象方法,只要都实现了就行;但Lambda只能为单抽象方法的接口创建实例
      • 匿名内部类除了可以为接口创建实例,还可以是抽象类普通类;但Lambda只能函数式接口
      • 匿名内部类实现抽象方法的方法体中可以调用默认方法,但Lambda代码块中不可以。见上面的示例代码

    Lambda表达式的应用

    • 多用于返回一个某函数式接口的对象
    • 见示例:
    package testpack;
    import java.util.Arrays;
    public class Test1{  
        public static void main(String[] args) { 
        	String[] arr1=new String[]{"iOS","android","Java","C#","C","C++"};
        	System.out.println(Arrays.toString(arr1));
        	Arrays.parallelSort(arr1,(o1,o2)->o1.length()-o2.length());  //parallelSort(T[] a, Comparator<? super T> cmp)。Lambda的对象是Comparator类型
        	System.out.println(Arrays.toString(arr1));
        	
        	int[] arr2=new int[]{2,6,-25,30,13,16};
        	System.out.println(Arrays.toString(arr2));
        	Arrays.parallelPrefix(arr2,(left,right)->left*right);   // 	parallelPrefix(int[] array, IntBinaryOperator op)。Lambda的对象是IntBinaryOperator类型
        	System.out.println(Arrays.toString(arr2));
        	
        	long[] arr3=new long[5];
        	Arrays.parallelSetAll(arr3,operand->operand*5);  //parallelSetAll(long[] array, IntToLongFunction generator)。Lambda的对象是 IntToLongFunction类型
        	System.out.println(Arrays.toString(arr3));
    
        	//以上的Comparator、IntBinaryOperator、IntToLongFunction都是函数式接口,都有@FunctionalInterface注解
        }
    }
    
    • 输出:

    [iOS, android, Java, C#, C, C++]
    [C, C#, iOS, C++, Java, android]
    [2, 6, -25, 30, 13, 16]
    [2, 12, -300, -9000, -117000, -1872000]
    [0, 5, 10, 15, 20]

  • 相关阅读:
    namenode无法自动切换的问题
    HDFS 安全模式的理解
    程序启动-Runloop
    浅谈MVC和MVVM模式
    Runtime 运行时之一:消息转发
    Runtime 运行时之一:消息传递
    Runtime 运行时之一:类与对象
    GCC 编译详解
    UIView中的坐标转换
    Xcode 利用VVDocumenter 生成注释 通过设置 再生成注释文档
  • 原文地址:https://www.cnblogs.com/sonng/p/6091058.html
Copyright © 2011-2022 走看看