Java 8 新特性之Lambda表达式

Java 8 新特性&Lambda表达式

接口中的默认方法

Java 8 允许我们给接口中添加一个非抽象的方法,只需要使用default关键字定义即可。
接口中还可以存在静态方法,可以使用 接口名.静态方法名 的形式直接使用

1. Lambda 表达式

1.1 认识Lambda表达式

public class LambdaTest1{
	public static void main(String[] args) {
		//假如一个list集合中的元素要排序
		List<String> list = Arrays.asList("hello","tom","apple","bbc");
		//之前的排序我们可以这样写
		Collections.sort(list, new Comparator<String>(){
			@Override
			public int compare(String o1, String o2) {
				return -o1.compareTo(o2);
			}
		});
			
		//使用Lambda表达式
		Collections.sort(list,(String s1,String s2)->{
			return s1.compareTo(s2);
		});
		//可以简写为下面这一行
		Collections.sort(list,(s1,s2)->s1.compareTo(s2));
		
		System.out.println(list);	
		}
}

java中,引入了一个新的操作符“->”,该操作符在很多资料中,称为箭头操作符,或者lambda操作符;箭头操作符将lambda分成了两个部分:
左侧:lambda表达式的参数列表
右侧:lambda表达式中所需要执行的功能,即lambda函数体,和返回值类型

1.2 Functional接口

函数式接口:
有且只有一个抽象方法的接口,但是可以另外有其他的默认方法(非抽象方法)和静态方法
【Lambda表达式】本质就是对【函数式接口】中唯一 一个抽象方法进行实现的。

因为 默认方法 不算抽象方法,所以你也可以给你的函数式接口添加默认方法。

注意:有多个抽象方法的接口,不能实现lambda表达式

如何保证一个接口是【函数式接口】呢?
在接口上方加上 @FunctionalInterface 注解,如果不满足,此接口就会报错。

例如

//这个注解不加也可以,加上只是为了让编译器检查
@FunctionalInterface
interface Action{
	public void run();
	default void doSomething(){
		System.out.println("doSomething..");
	}
}

@FunctionalInterface
interface Work<T,V>{
	public V doWork(T t);
}
//类
public class LambdaTest2 {
		
		public static void test(Action a){
			a.run();
			a.doSomething();
		}
		
		public static void run(Work<String,Integer> a){
			int i = a.doWork("hello");
			System.out.println(i);
		}

		public static void main(String[] args) {
			//原来的内部类实现方式
			test(new Action(){
				@Override
				public void run() {
					System.out.println("run..");
				}
			});
			
			//1.lambda表达式方法
			test(()->System.out.println("run"));

			//2.也可以先创建对象
			Action a = ()->System.out.println("run...");
			System.out.println(a.getClass());
			test(a);

			//接口中有泛型也可以,只关注方法的参数和返回值
			Work<String,Integer> w = (v)->v.length();
			run(w);

			run((v)->v.length());

			//如果参数只有一个,那么还可以这样简写: 去掉小括号
			//注意代码就一句,作为返回值的话不用写return
			run(v->v.length());
			
			//有多句代码,就需要写{}了,并且需要写return
			run(v->{
				System.out.println("doWork..");
				return v.length();
			});
		}
		
	}

注意:lambda表达式无法表示接口中默认方法的重写,lambda表达式只能去匹配对应接口中的唯一抽象方法。相当于lambda表达式只是对抽象方法的实现。

静态方法和非静态方法的引用

Java 8 允许使用 :: 关键字来传递方法(静态方法和非静态方法)

能引用的条件:
	Action接口中只有一个方法且方法的  参数类型  和  返回值类型  与Integer类中的
	静态方法toBinaryString的  参数类型、返回类型  是一致的.
	那么能引用非静态方法的原因也是这样

public class LambdaTest3 {
		
		public static void main(String[] args) {
			
			//正常是这样使用的
			Action a1 = v->"接收的数字为:"+v;
			System.out.println(a1.run(5));

			//使用Lambda引用Integer类中的静态方法
			Action a2 = Integer::toBinaryString;
			System.out.println(a2.run(10));
			
			//使用Lambda引用LambdaTest3类的对象中的非静态方法
			LambdaTest3 t = new LambdaTest3();
			Action a3 = t::test;
			System.out.println(a3.run(20));
		}
		
		public String test(int i){
			return "i="+i;
		}
		@FunctionalInterface
		interface Action{
			public String run(int i);
		}
}
	

下面是一个接口中带泛型的时候例子: 可以使用 类名::非静态方法 的形式引用方法

public class LambdaTest3Pro {
	
		public static void main(String[] args) {
	
			Model m = new Model();
			
			//这些写法都可以
			//相当于变量v是run方法中接收的形参,然后使用v调用它的方法,v的类型是Model,因为这里使用了泛型
			Action3Pro<Model> a1 = v->v.test1();
			Action3Pro<Model> a1 = v->v.test2("hello");
			Action3Pro<Model> a1 = v->v.test3();
			a1.run(m);
			
			//在这种情况下,还可以使用Model引用它的内部的方法
			//但是必须满足以下要求:
			//1.实现的抽象方法(run)参数类型【必须】Model
			//2.引用的方法【必须】是无参的
			//将来run方法中就自动会调用所传的对象m这个被引用的方法
			Action3Pro<Model> a2 = Model::test1;
			a2.run(m);
			或者
			Action3Pro<Model> a2 = Model::test3;
			a2.run(m);

			//编译报错,因为test2不是无参的
			Action3Pro<Model> a2 = Model::test2;
			a2.run(m);

		}
		
	}

	interface Action3Pro<T>{
		public void run(T t);
	}

	class Model{
		
		//无参无返回值类型
		public void test1(){
			System.out.println("test1");
		}
		//有参数没返回值
		public void test2(String s){
			System.out.println("test2");
		}
		无参有返回值类型
		public int test3(){
			System.out.println("test3");
			return 1;
		}
	}

使用 :: 关键字引用构造函数

public class LambdaTest4 {
		
		public static void main(String[] args) {
			
			//Lambda表达式引用构造函数
			//根据构造器的参数来自动匹配使用哪一个构造器
			//这里执行create方法的时候会自动调用Action4类中的有参构造器
			Action4Creater a = Action4::new;  
			Action4 a4 = a.create("zhangsan");
			a4.say();	
		}
		
	}

	class Action4{
		private String name;
		public Action4() {	
		}
		public Action4(String name) {
			this.name = name;
		}
		public void say(){
			System.out.println("name = "+name);
		}
	}

	interface Action4Creater{
		public Action4 create(String name);
	}

常用的函数式接口

在jdk中通用的函数式接口如下(都在java.util.function包中):

Supplier<String> sp = () -> "hello";//只有输出消息,没有输入参数
Consumer<String> cp = r -> System.out.printf(r);//有一个输入参数,没有输出
Function<Integer, String> func = r -> String.valueOf(r);//有一个输入参数 有一个输出参数
BiFunction<Integer, Integer, String> biFunc = (a, b) -> String.valueOf(a + b);//有两个输入参数 有一个输出参数
BiConsumer<Integer, Integer> biCp = (a, b) -> System.out.printf(String.valueOf(a + b));//有两个输入参数 没有输出参数

java.util.function.Function<T, R> 接口

Function接口有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法
compose方法表示在某个方法之前执行
andThen方法表示在某个方法之后执行
注意:compose和andThen方法调用之后都会把对象自己本身返回,这可以方便链式编程

public interface Function<T, R> {

	R apply(T t);

	default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
		Objects.requireNonNull(before);
		return (V v) -> apply(before.apply(v));
	}

	default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
		Objects.requireNonNull(after);
		return (T t) -> after.apply(apply(t));
	}
			
	//注意: t->t是(t)->t的简写
	//t->t是作为方法identity的返回值的,也就是Function类型对象
	//类似于这样的写法:Function<Object, Object> f = t->t;
	//那么f.apply("test") 返回字符串"test"
	//传入什么则返回什么
	static <T> Function<T, T> identity() {
		return t -> t;
	}
}

java.util.function.Supplier接口

Supplier接口返回一个任意范型的值,和Function接口不同的是该接口没有任何参数

public interface Supplier<T> {
		T get();
	}
public class SupplierTest {
		public static void main(String[] args) {
			//生成一个八位的随机字符串
			Supplier<String> f = ()->{
				String base = "abcdefghijklmnopqrstuvwxyz0123456789";     
				Random random = new Random();     
				StringBuffer sb = new StringBuffer();     
				for (int i = 0; i < 8; i++) {  
					//生成[0,base.length)之间的随机数
					int number = random.nextInt(base.length());     
					sb.append(base.charAt(number));     
				}     
				return sb.toString();   
			};

			System.out.println(f.get());
		}
		
	}

java.util.function.Consumer接口

Consumer接口接收一个任意范型的值,和Function接口不同的是该接口没有任何值

public interface Consumer<T> {

		void accept(T t);

		default Consumer<T> andThen(Consumer<? super T> after) {
			Objects.requireNonNull(after);
			return (T t) -> { accept(t); after.accept(t); };
		}
	}

例如:

public class ConsumerTest {
		//静态内部类
		private static class Student{
			private String name;
			public String getName() {
				return name;
			}
			public void setName(String name) {
				this.name = name;
			}
		}
		
		public static void main(String[] args) {
			Student s = new Student();
			s.setName("tom");
			
			Consumer<Student> first = stu->{
				System.out.println("我要第一个执行,把name值给改了");
				stu.setName("zhangsan");
			};
			Consumer<Student> then = stu->{
				System.out.println("我紧跟着执行,输出的name值为:"+stu.getName());
			};
			first.andThen(then).accept(s);
			
		}
		
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YJY@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值