Java8特性
接口定義增強
JDK1.8之前,接口的組成只有全局變量
和抽象方法
。從JDK1.8開始,接口的組成增加了。
假設:現有一個接口,其子類有2W個。現在發現該接口功能不足,要增加一個方法,該方法對於所有子類而言的功能是一樣的(即方法體是一樣的)。此時要修改每個子類的方法,要修改2W次。
上述問題在JDK1.8中不存在,因爲其允許接口中定義普通方法,但普通方法必須使用default定義。
interface Fruit {
public void print(); // 接口原本定義的方法
default public void fun() { // 普通方法
System.out.println("JDK1.8");
}
}
class Apple implements Fruit {
@Override
public void print() {
System.out.println("蘋果");
}
}
public class Demo {
public static void main(String[] args) {
Fruit f = new Apple();
f.fun();
f.print();
}
}
除了使用default
定義方法,還可以使用stati
c定義方法
範例:定義static方法
interface Fruit {
public void print(); // 接口原本定義的方法
default public void fun() { // 普通方法
System.out.println("JDK1.8");
}
static void get() {
System.out.println("直接由接口調用");
}
}
class Apple implements Fruit {
@Override
public void print() {
System.out.println("蘋果");
}
}
public class Demo {
public static void main(String[] args) {
Fruit f = new Apple();
f.fun();
f.print();
Fruit.get();
}
}
JDK1.8有個新功能:內部類訪問方法參數時可以不加上final關鍵字。
這些新特性,完全打破了Java已有的代碼組成形式。
Lamda表達式
Lamda屬於函數式編程的概念,下面通過匿名內部類,來分析函數式編程的產生目的。
範例:匿名內部類
interface Fruit {
public void print(); // 接口原本定義的方法
}
public class Demo {
public static void main(String[] args) {
fun(new Fruit() {
@Override
public void print() {
System.out.println("水果");
}
});
}
public static void fun(Fruit fru) {
fru.print();
}
}
上述代碼中fun()最終需要的只是輸出,但是由於Java的開發結構的完整性要求,不得不在這個核心語句上嵌套更多的內容。但是該做法過於嚴謹複雜,在JDK1.8引入函數式編程,簡化過程。
範例:使用Lamda表達式
interface Fruit {
public void print(); // 接口原本定義的方法
}
public class Demo {
public static void main(String[] args) {
fun(()-> System.out.println("水果"));
}
public static void fun(Fruit fru) {
fru.print();
}
}
Lamda語法有三種形式:
·(參數)->單行語句;
·(參數)->{單行語句};
·(參數)->表達式;
範例:觀察有參單行
interface Fruit {
public void print(String str); // 接口原本定義的方法
}
public class Demo {
public static void main(String[] args) {
// 首先要定義此表達式裏面需要接收變量,單行語句直接進行輸出
fun((s) -> System.out.println(s));
}
public static void fun(Fruit fru) {
fru.print("蘋果"); // 設置參數的內容
}
}
範例:編寫多行語句
interface Fruit {
public void print(String str); // 接口原本定義的方法
}
public class Demo {
public static void main(String[] args) {
// 首先要定義此表達式裏面需要接收變量,單行語句直接進行輸出
fun((s) -> {
s = s.toUpperCase();
System.out.println(s);
});
}
public static void fun(Fruit fru) {
fru.print("Hello"); // 設置參數的內容
}
}
範例:編寫表達式
interface Fruit {
public int add(int x, int y);
}
public class Demo {
public static void main(String[] args) {
// 首先要定義此表達式裏面需要接收變量,單行語句直接進行輸出
fun((s1, s2) -> s1 + s2);
}
public static void fun(Fruit fru) {
System.out.println(fru.add(10, 20));
}
}
方法引用
對象引用的特點:不同對象可以操作同一塊內容。而方法引用就是指爲一個方法設置別名,相當於一個方法定義了不同的名字。
1、方法引用在Java8中一共定義了四種形式:
· 引用靜態方法: 類名稱::static 方法名稱;
· 引用某個對象的方法:實例化對象::普通方法;
· 引用特定類型的方法:特定類::普通方法;
· 引用構造方法:類名稱::new。
範例:引用靜態方法
String類中的valueof():public static String valueof(int x)
package com.java.demo;
/**
* 只有一個方法的接口
* @param <P> 參數的數據類型
* @param <R> 返回值的數據類型
*/
interface Math<P, R> {
public R exchange(P p);
}
public class Demo {
public static void main(String[] args) {
// 覆寫了exchange(),使其具有valueOf()的功能
Math<Integer, String> math = String::valueOf;
String msg = math.exchange(10000);
// 將所有的0替換成9
System.out.println(msg.replaceAll("0", "9"));
}
}
範例:普通方法引用
String類中的toUpperCase():public String toUpperCase()
package com.java.demo;
interface Math<R> {
public R upper();
}
public class Demo {
public static void main(String[] args) {
// 覆寫了upper,使其具有toUpperCase的功能
// toUpperCase是普通方法,必須由String對象調用
// hello是String對象,代碼如下
Math<String> math = "hello"::toUpperCase;
String msg = math.upper();
System.out.println(msg);
}
}
上述例子顯示,要實現方法引用,必須要有接口,且該接口只能有一個方法。爲了保證該接口只有一個方法,可對其進行註解說明。
@FunctionalInterface // 此爲函數式接口,只能定義一個方法
interface ITest<R> {
public R upper();
}
2、在進行方法引用的過程中,還有一種形式的引用(這種形式需要特定類的對象支持)。一般使用“類::方法”,引用的是類中的靜態方法。但是這種形式也可以引用普通方法。
例如:在String類中有一個方法:public int compareTo(String anotherString)
,比較的形式是String對象1.compareTo(String對象2)
,即要引用該方法,需要有兩個參數。
範例:引用特定類的方法
interface IMessage<P> {
public int compare(P p1, P p2);
}
public class Demo {
public static void main(String[] args) {
IMessage<String> msg = String::compareTo;
System.out.println(msg.compare("A", "B"));
}
}
與之前相比,方法引用前不需要定義對象,可以理解爲將對象定義在參數上。
範例:引用構造方法
interface IMessage<C> {
public C create(String t, double p);
}
class Book {
private String title;
private double price;
public Book(String title, double price) {
this.title = title;
this.price = price;
}
public String toString() {
return "書名:" + this.title + ",價格:" + this.price;
}
}
public class Demo {
public static void main(String[] args) {
IMessage<Book> msg = Book::new; // 引用構造方法
// 雖然調用的是create(),實際引用了Book類的構造方法
Book book = msg.create("Java開發", 66.6);
System.out.println(book);
}
}
對象引用是使用不同的名字,而方法引用需要一個函數接口。
內建函數式接口
1、JDK1.8中提供了一個包:java.util.function
,提供有以下四個核心接口:
(1)功能性接口(Function):public interface Function<T,R>{public R apply(T t);}
|- 此接口需要接收一個參數,並且返回一個處理結果;
(2)消費型接口(Consumer):public interface Consumer{public void accept(T t);}
|- 此接口只負責接收數據(引用數據是不需要返回的),並且不返回結果;
(3)供給型接口(Suplier):public interface Supplier{public T get();}
|- 此接口不接收參數,但是可以返回結果
(4)斷言型接口(Predicate):public interface Predicate{public boolean Test(T t);}
|- 進行判斷操作使用;
在JDK1.8中存在以上四個功能型接口,所以很少會由用戶去定義新的函數式接口。
範例:函數式接口——接收參數並返回處理結果
· String類有一個方法:public boolean startsWith(String str)
package com.java.demo;
import java.util.function.Function;
public class Demo {
public static void main(String[] args) {
Function<String, Boolean> fun = "##hello"::startsWith;
System.out.println(fun.apply("##")); // true
}
}
範例:消費型接口
package com.java.demo;
import java.util.function.Consumer;
class MyDemo {
// 此方法沒有返回值,但是有參數
public void print(String str) {
System.out.println(str);
}
}
public class Demo {
public static void main(String[] args) {
Consumer<String> cons = new MyDemo()::print;
cons.accept("Hello World");
}
}
範例:供給型接口
· 引用String類的toUpperCase():public String toUpperCase()
;
package com.java.demo;
import java.util.function.Supplier;
public class Demo {
public static void main(String[] args) {
Supplier<String> sup = "hello"::toUpperCase;
System.out.println(sup.get());
}
}
範例:斷言型接口
· String類中有equalsIgnoreCase()
import java.util.function.Predicate;
public class Demo {
public static void main(String[] args) {
Predicate<String> pre = "hello"::equalsIgnoreCase;
System.out.println(pre.test("Hello"));
}
}