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);
}
}html

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));html5

注意:如今接口還能夠存在靜態方法,可使用 接口名.靜態方法名 的形式直接調用java


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);
}app

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);
}dom

public static void main(String[] args) {

//原來的內部類實現方式
test(new Action(){
@Override
public void run() {
System.out.println("run..");
}
});

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


//也能夠先建立對象
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")); }

相關文章
相關標籤/搜索