java_day12_jdk1.8新特性

1.接口的默认方法
Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用default关键字即可,这个又叫做扩展方法
//Formula表示一个设计 计算公式 的接口
public interface Formula {
//计算
public double calculate(int a);

//开方
default double sqrt(int a){
return Math.sqrt(a);
}
}

main:
Formula f = new Formula() {
@Override
public double calculate(int a) {
return a+1;
}
};
System.out.println(f.calculate(4));
System.out.println(f.sqrt(8));

注意:现在接口还可以存在静态方法,可以使用 接口名.静态方法名 的形式直接调用


2.Lambda 表达式
2.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);
});

//可以简写为
//1.大括号里面就一句代码
//2.编译器可以自动推导出参数类型
Collections.sort(list,(s1,s2)->s1.compareTo(s2));

System.out.println(list);

}

}


2.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..");
}
});

//lambda表达式方法
test(()->System.out.println("run"));


//也可以先创建对象
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();
});

//观察下面代码是什么意思
run(v->1);

}

}

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


2.3 方法与构造函数引用
Java 8 允许你使用 :: 关键字来传递方法(静态方法和非静态方法)
例如:
public class LambdaTest3 {

public static void main(String[] args) {

//正常是这样使用的
Action3 a1 = v->"接收的数字为:"+v;
System.out.println(a1.run(5));

//使用Lambda引用Integer类中的静态方法
Action3 a2 = Integer::toBinaryString;
System.out.println(a2.run(10));

//使用Lambda引用LambdaTest3类的对象中的非静态方法
LambdaTest3 t = new LambdaTest3();
Action3 a3 = t::test;
System.out.println(a3.run(20));
}

public String test(int i){
return "i="+i;
}

}
@FunctionalInterface
interface Action3{
public String run(int i);
}

注:相当于使用lambda表达式引用另一个类中方法的实现来作为Action3接口中run方法的实现,前提是俩个方法的参数列表和返回类型必须一致

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


下面是一个接口中带泛型的时候例子: 可以使用 类名::非静态方法 的形式引用方法
注意:没有泛型的时候都也使用
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;
}
}

Java 8 允许你使用 :: 关键字来引用构造函数
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);
}


2.4 lambda表达式中的变量访问
public class LambdaTest5 {
private static int j;
private int k;
public static void main(String[] args) {
LambdaTest5 t = new LambdaTest5();
t.test();
}

public void test(){
int num = 10;
j = 20;
k = 30;

//lambda表达式中可以访问成员变量也可以方法局部变量
Action5 a5 = (i)->System.out.println("操作后:i="+(i+num+j+k));
a5.run(1);

//但是这个被访问的变量默认变为final修饰的 不可再改变 否则编译不通过
//num = 60;
j = 50;
k = 70;
}

}

interface Action5{
public void run(int i);
}



2.5 java.util.function.Predicate<T>接口
Predicate接口是用来支持java函数式编程新增的一个接口,使用这个接口和lambda表达式就可以用更少的代码给API中的方法添加更多的动态行为。
public class PredicateTest {
public static void main(String[] args) {
List<String> list = Arrays.asList("Java", "html5","JavaScript", "C++", "hibernate", "PHP");

//全都显示
filter(list, name->true);
//全都不显示
filter(list, name->false);

//开头是J的语言
filter(list,name->name.startsWith("J"));
//5结尾的
filter(list,name->name.endsWith("5"));

//显示名字长度大于4
filter(list,name->name.length()>4);

System.out.println("-----------------------");
//名字以J开头并且长度大于4的
Predicate<String> c1 = name->name.startsWith("J");
Predicate<String> c2 = name->name.length()>4;
filter(list,c1.and(c2));

//名字不是以J开头
Predicate<String> c3 = (name)->name.startsWith("J");
filter(list,c3.negate());

//名字以J开头或者长度小于4的
Predicate<String> c4 = name->name.startsWith("J");
Predicate<String> c5 = name->name.length()<4;
filter(list,c4.or(c5));

//也可以直接使用Predicate接口中的静态方法
//名字为Java的
filter(list,Predicate.isEqual("Java"));

//判断俩个字符串是否相等
boolean test = Predicate.isEqual("hello").test("world");
System.out.println(test);
}
public static void filter(List<String> list, Predicate<String> condition) {
for(String name: list) {
if(condition.test(name)) {
System.out.println(name + " ");
}
}
}

}

2.6 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;
}
}

例如:
public class FunctionTest {
//静态内部类
private static class Student{
private String name;
public Student(String name){
this.name = name;
}
public String getName() {
return name;
}

}
public static void main(String[] args) {
/*用户注册输入一个名字tom*/
String name = "tom";

/*使用用户的输入的名字创建一个对象*/
Function<String, Student> f1 =(s)->new Student(s);
//注意上面的代码也可以写出这样,引用类中的构造器
//Function<String, Student> f1 =Student::new;
Student stu1 = f1.apply(name);
System.out.println(stu1.getName());

/*需求改变,使用name创建Student对象之前需要给name加一个前缀*/
Function<String,String> before = (s)->"briup_"+s;
//表示f1调用之前先执行before对象的方法,把before对象的方法返回结果作为f1对象方法的参数
Student stu2 = f1.compose(before).apply(name);
System.out.println(stu2.getName());

/*获得创建好的对象中的名字的长度*/
Function<Student,Integer> after = (stu)->stu.getName().length();
//before先调用方法,结果作为参数传给f1来调用方法,f1调用完方法后的结果再作为参数传给after,结果就是我们接收的数据
int len = f1.compose(before).andThen(after).apply(name);
System.out.println(len);

}

}

2.7 java.util.function.Supplier<T>接口
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());
}

}


2.8 java.util.function.Consumer<T>接口
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);

}

}

总结:
Function<T, R> 接口 R apply(T t); 有参数有返回值
Supplier<T> 接口 T get(); 没参数有返回值
Consumer<T> 接口 void accept(T t); 有参数没返回值

另外需要注意的接口: 其用法和上面介绍的接口使用方式类同
BinaryOperator<T>接口 T apply(T t, T t) 将两个T作为输入,返回一个T作为输出
注:一般用于同一类型的数据的合并
BiFunction<T, U, R>接口 R apply(T t, U u) 将一个T和一个U输入,返回一个R作为输出
注:俩种不同类型的数据,操作后得到第三种类型的数据

注:BinaryOperator接口继承了BiFunction接口
public interface BinaryOperator<T> extends BiFunction<T,T,T>

BiConsumer<T, U>接口 void accept(T t, U u) 将俩个参数传入,没有返回值


注:可以根据具体的功能需求,来自定义出自己的函数式接口


2.9 java.util.Optional<T>类
Optional 不是接口而是一个类,这是个用来防止NullPointerException异常的辅助类型
Optional 被定义为一个简单的容器,其值可能是null或者不是null。在Java8之前一般某个函数应该返回非空对象但是偶尔却可能返回了null,而在Java8中,不推荐你返回null而是返回Optional。
这是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
public class OptionalTest {

public static void main(String[] args) {

/*of方法 给非null的值创建一个Optional对象*/
//需要注意的是,创建对象时传入的参数不能为null。
//如果传入参数为null,则抛出NullPointerException 。
Optional<String> op1 = Optional.of("hello");

/*ofNullable方法 为指定的值创建一个Optional,如果指定的值为null,则返回一个空的Optional。*/
//ofNullable与of方法相似,唯一的区别是可以接受参数为null的情况
Optional<String> op2 = Optional.ofNullable(null);

/*isPresent方法 如果值存在返回true,否则返回false。*/
/*get方法 如果Optional有值则将其返回,否则抛出NoSuchElementException。*/
if(op1.isPresent()){
System.out.println(op1.get());
}
if(op2.isPresent()){
System.out.println(op2.get());
}

/*ifPresent方法 如果Optional实例有值则为其调用consumer接口中的方法,否则不做处理*/
//consumer接口中的方法只有参数没有返回值
//public void accept(T t);

op1.ifPresent(str->System.out.println(str));
op2.ifPresent(str->System.out.println(str));//这个不执行 因为op2里面的值是null


/*orElse方法 如果有值则将其返回,否则返回指定的其它值。*/
System.out.println(op1.orElse("如果op1中的值为null则返回这句话"));
System.out.println(op2.orElse("如果op2中的值为null则返回这句话"));


/*orElseGet方法 orElseGet与orElse方法类似,区别在于得到的默认值的方式不同。orElse方法将传入的字符串作为默认值,orElseGet方法可以接受Supplier接口的实现用来生成默认值。*/
//Supplier接口中的方法没有参数但是有返回值
//public T get();
System.out.println(op1.orElseGet(()->"自己定义的返回值"));
System.out.println(op2.orElseGet(()->"自己定义的返回值"));


/*orElseThrow方法 如果有值则将其返回,否则抛出supplier接口创建的异常。*/
//在orElseThrow中我们可以传入一个lambda表达式或方法,如果值不存在来抛出异常。
//orElseThrow方法的声明如下 所有只能返回一个Throwable类型对象
//public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X

try {
System.out.println(op1.orElseThrow(Exception::new));
//System.out.println(op2.orElseThrow(NullPointerException::new));这个会抛出异常
} catch (Exception e) {
e.printStackTrace();
}


/*map方法 如果有值,则调用mapper的函数处理并得到返回值*/
//返回值并且依然Optional包裹起来,其泛型和你返回值的类型一致
//public<U> Optional<U> map(Function<? super T, ? extends U> mapper)

Optional<Integer> map1 = op1.map(str->1);
System.out.println(map1.orElse(0));
Optional<Double> map2 = op2.map(str->1.2);
System.out.println(map2.orElse(0D));


/*flatMap方法 如果有值,则调用mapper的函数返回Optional类型返回值,否则返回空Optional。*/
//flatMap与map方法类似,区别在于flatMap中的mapper返回值必须是Optional。
//调用结束时,flatMap不会对结果用Optional封装
//需要我们自己把返回值封装为Optional
//public<U> Optional<U> flatMap(Function<? super T,
Optional<U>> mapper);

Optional<String> flatMap = op1.flatMap(str->Optional.of(str+"_briup"));
System.out.println(flatMap.get());
//编译出错,因为lambda表示的函数的返回类型不对
//op1.flatMap(str->"");

/*filter方法 如果有值并且满足断言条件返回包含该值的Optional,否则返回空Optional。*/
//public Optional<T> filter(Predicate<? super T> predicate);

op1 = op1.filter(str->str.length()<10);
System.out.println(op1.orElse("值为null"));
op1 = op1.filter(str->str.length()>10);
System.out.println(op1.orElse("值为null"));
}

猜你喜欢

转载自www.cnblogs.com/yue-170305/p/11478939.html