java8函數式介面
⑴ java中lambda表達式怎樣實現一個多方法的介面呢
並不是所有介面都可以使用Lambda表達式,只有函數式介面可以。
按照Java8函數式介面的定義,其只能有一個抽象方法,否則就不是函數時介面,就無法用Lambda表達式。
可以使用@FunctionalInterface標注函數式介面,在編譯時提前發現錯誤。
⑵ Java8的函數式編程怎麼樣
使用函數式代碼的好處:
減少了可變數(Immutable Variable)的聲明
能夠更好的利用並行(Parallelism)
代碼更加簡潔和可讀
函數式介面
函數式介面就是僅聲明了一個方法的介面,比如我們熟悉的Runnable,Callable,Comparable等都可以作為函數式介面。當然,在Java 8中,新添加了一類函數式介面,如Function,Predicate,Consumer,Supplier等。
⑶ java 8 哪些介面支持 lambda
以本人遇見,Lambda表達式主要為了簡化只包含一個函數的匿名類。 同時好像印象里聽說過,Java8中把只包含一個函數敘述的介面稱之為「函數式介面」 比如咱們常見的Runnable只有一個run,就可以說它是一個函數式介面。 對於此類介面,使用Lambda前:...
⑷ 如圖是使用java8的lambda表達式和函數式介面實現的一個方法,請問如果使用java7實現,圖示入參怎麼定義
public class Predicte{
boolean evaluate(Mole m);
}
把判斷函數用類包裝起來
然後在對moles遍歷時調用這個方法過濾?
⑸ Java8的特性有哪些
1、函數式介面
Java 8 引入的一個核心概念是函數式介面(Functional Interfaces)。通過在介面裡面添加一個抽象方法,這些方法可以直接從介面中運行。如果一個介面定義個唯一一個抽象方法,那麼這個介面就成為函數式介面。同時,引入了一個新的註解:@FunctionalInterface。可以把他它放在一個介面前,表示這個介面是一個函數式介面。這個註解是非必須的,只要介面只包含一個方法的介面,虛擬機會自動判斷,不過最好在介面上使用註解 @FunctionalInterface 進行聲明。在介面中添加了 @FunctionalInterface 的介面,只允許有一個抽象方法,否則編譯器也會報錯。
java.lang.Runnable 就是一個函數式介面。
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
2、Lambda 表達式
函數式介面的重要屬性是:我們能夠使用 Lambda 實例化它們,Lambda 表達式讓你能夠將函數作為方法參數,或者將代碼作為數據對待。Lambda 表達式的引入給開發者帶來了不少優點:在 Java 8 之前,匿名內部類,監聽器和事件處理器的使用都顯得很冗長,代碼可讀性很差,Lambda 表達式的應用則使代碼變得更加緊湊,可讀性增強;Lambda 表達式使並行操作大集合變得很方便,可以充分發揮多核 CPU 的優勢,更易於為多核處理器編寫代碼;
Lambda 表達式由三個部分組成:第一部分為一個括弧內用逗號分隔的形式參數,參數是函數式介面裡面方法的參數;第二部分為一個箭頭符號:->;第三部分為方法體,可以是表達式和代碼塊。語法如下:
1. 方法體為表達式,該表達式的值作為返回值返回。
(parameters) -> expression
2. 方法體為代碼塊,必須用 {} 來包裹起來,且需要一個 return 返回值,但若函數式介面裡面方法返回值是 void,則無需返回值。
(parameters) -> { statements; }
例如,下面是使用匿名內部類和 Lambda 表達式的代碼比較。
下面是用匿名內部類的代碼:
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.print("Helllo Lambda in actionPerformed");
}
});
下面是使用 Lambda 表達式後:
button.addActionListener(
\\actionPerformed 有一個參數 e 傳入,所以用 (ActionEvent e)
(ActionEvent e)->
System.out.print("Helllo Lambda in actionPerformed")
);
上面是方法體包含了參數傳入 (ActionEvent e),如果沒有參數則只需 ( ),例如 Thread 中的 run 方法就沒有參數傳入,當它使用 Lambda 表達式後:
Thread t = new Thread(
\\run 沒有參數傳入,所以用 (), 後面用 {} 包起方法體
() -> {
System.out.println("Hello from a thread in run");
}
);
通過上面兩個代碼的比較可以發現使用 Lambda 表達式可以簡化代碼,並提高代碼的可讀性。
為了進一步簡化 Lambda 表達式,可以使用方法引用。例如,下面三種分別是使用內部類,使用 Lambda 表示式和使用方法引用方式的比較:
//1. 使用內部類
Function<Integer, String> f = new Function<Integer,String>(){
@Override
public String apply(Integer t) {
return null;
}
};
//2. 使用 Lambda 表達式
Function<Integer, String> f2 = (t)->String.valueOf(t);
//3. 使用方法引用的方式
Function<Integer, String> f1 = String::valueOf;
要使用 Lambda 表達式,需要定義一個函數式介面,這樣往往會讓程序充斥著過量的僅為 Lambda 表達式服務的函數式介面。為了減少這樣過量的函數式介面,Java 8 在 java.util.function 中增加了不少新的函數式通用介面。例如:
Function<T, R>:將 T 作為輸入,返回 R 作為輸出,他還包含了和其他函數組合的默認方法。
Predicate<T> :將 T 作為輸入,返回一個布爾值作為輸出,該介麵包含多種默認方法來將 Predicate 組合成其他復雜的邏輯(與、或、非)。
Consumer<T> :將 T 作為輸入,不返回任何內容,表示在單個參數上的操作。
例如,People 類中有一個方法 getMaleList 需要獲取男性的列表,這里需要定義一個函數式介面 PersonInterface:
interface PersonInterface {
public boolean test(Person person);
}
public class People {
private List<Person> persons= new ArrayList<Person>();
public List<Person> getMaleList(PersonInterface filter) {
List<Person> res = new ArrayList<Person>();
persons.forEach(
(Person person) ->
{
if (filter.test(person)) {//調用 PersonInterface 的方法
res.add(person);
}
}
);
return res;
}
}
為了去除 PersonInterface 這個函數式介面,可以用通用函數式介面 Predicate 替代如下:
class People{
private List<Person> persons= new ArrayList<Person>();
public List<Person> getMaleList(Predicate<Person> predicate) {
List<Person> res = new ArrayList<Person>();
persons.forEach(
person -> {
if (predicate.test(person)) {//調用 Predicate 的抽象方法 test
res.add(person);
}
});
return res;
}
}
3、介面的增強
Java 8 對介面做了進一步的增強。在介面中可以添加使用 default 關鍵字修飾的非抽象方法。還可以在介面中定義靜態方法。如今,介面看上去與抽象類的功能越來越類似了。
默認方法
Java 8 還允許我們給介面添加一個非抽象的方法實現,只需要使用 default 關鍵字即可,這個特徵又叫做擴展方法。在實現該介面時,該默認擴展方法在子類上可以直接使用,它的使用方式類似於抽象類中非抽象成員方法。但擴展方法不能夠重載 Object 中的方法。例如:toString、equals、 hashCode 不能在介面中被重載。
例如,下面介面中定義了一個默認方法 count(),該方法可以在子類中直接使用。
public interface DefaultFunInterface {
//定義默認方法 countdefault int count(){
return 1;
}
}
public class SubDefaultFunClass implements DefaultFunInterface {
public static void main(String[] args){
//實例化一個子類對象,改子類對象可以直接調用父介面中的默認方法 count
SubDefaultFunClass sub = new SubDefaultFunClass();
sub.count();
}
}
靜態方法
在介面中,還允許定義靜態的方法。介面中的靜態方法可以直接用介面來調用。
例如,下面介面中定義了一個靜態方法 find,該方法可以直接用 StaticFunInterface .find() 來調用。
public interface StaticFunInterface {public static int find(){
return 1;
}
}
public class TestStaticFun {
public static void main(String[] args){
//介面中定義了靜態方法 find 直接被調用
StaticFunInterface.fine();
}
}
⑹ java 為什麼引入函數式介面而不是委託
一、Lambda表達式Lambda表達式可以說是Java 8最大的賣點,她將函數式編程引入了Java。Lambda允許把函數作為一個方法的參數,或者把代碼看成數據。一個Lambda表達式可以由用逗號分隔的參數列表、–符號與函數體三部分表示。例如:Arrays.asList( "p", "k", "u","f", "o", "r","k").forEach( e - System.out.println( e ) ); 1 Arrays.asList( "p", "k", "u","f", "o", "r","k").forEach( e - System.out.println( e ) ); 為了使現有函數更好的支持Lambda表達式,Java 8引入了函數式介面的概念。函數式介面就是只有一個方法的普通介面。java.lang.Runnable與java.util.concurrent.Callable是函數式介面最典型的例子。為此,Java 8增加了一種特殊的註解@FunctionalInterface:1 @FunctionalInterface2 public interface Functional {3 void method();4 }二、介面的默認方法與靜態方法我們可以在介面中定義默認方法,使用default關鍵字,並提供默認的實現。所有實現這個介面的類都會接受默認方法的實現,除非子類提供的自己的實現。例如:1 public interface DefaultFunctionInterface {2 default String defaultFunction() {3 return "default function";4 }5 }我們還可以在介面中定義靜態方法,使用static關鍵字,也可以提供實現。例如:1 public interface StaticFunctionInterface {2 static String staticFunction() {3 return "static function";4 }5 }介面的默認方法和靜態方法的引入,其實可以認為引入了C++中抽象類的理念,以後我們再也不用在每個實現類中都寫重復的代碼了。三、方法引用通常與Lambda表達式聯合使用,可以直接引用已有Java類或對象的方法。一般有四種不同的方法引用:構造器引用。語法是Class::new,或者更一般的Class T ::new,要求構造器方法是沒有參數;靜態方法引用。語法是Class::static_method,要求接受一個Class類型的參數;特定類的任意對象方法引用。它的語法是Class::method。要求方法是沒有參數的;特定對象的方法引用,它的語法是instance::method。要求方法接受一個參數,與3不同的地方在於,3是在列表元素上分別調用方法,而4是在某個對象上調用方法,將列表元素作為參數傳入;四、重復註解在Java 5中使用註解有一個限制,即相同的註解在同一位置只能聲明一次。Java 8引入重復註解,這樣相同的註解在同一地方也可以聲明多次。重復註解機制本身需要用@Repeatable註解。Java 8在編譯器層做了優化,相同註解會以集合的方式保存,因此底層的原理並沒有變化。五、擴展註解的支持Java 8擴展了註解的上下文,幾乎可以為任何東西添加註解,包括局部變數、泛型類、父類與介面的實現,連方法的異常也能添加註解。六、OptionalJava 8引入Optional類來防止空指針異常,Optional類最先是由Google的Guava項目引入的。Optional類實際上是個容器:它可以保存類型T的值,或者保存null。使用Optional類我們就不用顯式進行空指針檢查了。七、StreamStream API是把真正的函數式編程風格引入到Java中。其實簡單來說可以把Stream理解為MapRece,當然Google的MapRece的靈感也是來自函數式編程。她其實是一連串支持連續、並行聚集操作的元素。從語法上看,也很像linux的管道、或者鏈式編程,代碼寫起來簡潔明了,非常酷帥!八、Date/Time API (JSR 310)Java 8新的Date-Time API (JSR 310)受Joda-Time的影響,提供了新的java.time包,可以用來替代 java.util.Date和java.util.Calendar。一般會用到Clock、LocaleDate、LocalTime、LocaleDateTime、ZonedDateTime、Duration這些類,對於時間日期的改進還是非常不錯的。九、JavaScript引擎NashornNashorn允許在JVM上開發運行JavaScript應用,允許Java與JavaScript相互調用。十、Base64在Java 8中,Base64編碼成為了Java類庫的標准。Base64類同時還提供了對URL、MIME友好的編碼器與解碼器。除了這十大新特性之外,還有另外的一些新特性:更好的類型推測機制:Java 8在類型推測方面有了很大的提高,這就使代碼更整潔,不需要太多的強制類型轉換了。編譯器優化:Java 8將方法的參數名加入了位元組碼中,這樣在運行時通過反射就能獲取到參數名,只需要在編譯時使用-parameters參數。並行(parallel)數組:支持對數組進行並行處理,主要是parallelSort()方法,它可以在多核機器上極大提高數組排序的速度。並發(Concurrency):在新增Stream機制與Lambda的基礎之上,加入了一些新方法來支持聚集操作。Nashorn引擎jjs:基於Nashorn引擎的命令行工具。它接受一些JavaScript源代碼為參數,並且執行這些源代碼。類依賴分析器jdeps:可以顯示Java類的包級別或類級別的依賴。JVM的PermGen空間被移除:取代它的是Metaspace(JEP 122)。
⑺ Java9都快發布了,Java8的十大新特性你了解多少呢
一、Lambda表達式
Lambda表達式可以說是Java 8最大的賣點,她將函數式編程引入了Java。Lambda允許把函數作為一個方法的參數,或者把代碼看成數據。
一個Lambda表達式可以由用逗號分隔的參數列表、–>符號與函數體三部分表示。例如:
Arrays.asList( "p", "k", "u","f", "o", "r","k").forEach( e -> System.out.println( e ) );
1 Arrays.asList( "p", "k", "u","f", "o", "r","k").forEach( e -> System.out.println( e ) );
為了使現有函數更好的支持Lambda表達式,Java
8引入了函數式介面的概念。函數式介面就是只有一個方法的普通介面。java.lang.Runnable與java.util.concurrent.Callable是函數式介面最典型的例子。為此,Java
8增加了一種特殊的註解@FunctionalInterface:
1 @FunctionalInterface
2 public interface Functional {
3 void method();
4 }
二、介面的默認方法與靜態方法
我們可以在介面中定義默認方法,使用default關鍵字,並提供默認的實現。所有實現這個介面的類都會接受默認方法的實現,除非子類提供的自己的實現。例如:
1 public interface DefaultFunctionInterface {
2 default String defaultFunction() {
3 return "default function";
4 }
5 }
我們還可以在介面中定義靜態方法,使用static關鍵字,也可以提供實現。例如:
1 public interface StaticFunctionInterface {
2 static String staticFunction() {
3 return "static function";
4 }
5 }
介面的默認方法和靜態方法的引入,其實可以認為引入了C++中抽象類的理念,以後我們再也不用在每個實現類中都寫重復的代碼了。
三、方法引用
通常與Lambda表達式聯合使用,可以直接引用已有Java類或對象的方法。一般有四種不同的方法引用:
構造器引用。語法是Class::new,或者更一般的Class< T >::new,要求構造器方法是沒有參數;
靜態方法引用。語法是Class::static_method,要求接受一個Class類型的參數;
特定類的任意對象方法引用。它的語法是Class::method。要求方法是沒有參數的;
特定對象的方法引用,它的語法是instance::method。要求方法接受一個參數,與3不同的地方在於,3是在列表元素上分別調用方法,而4是在某個對象上調用方法,將列表元素作為參數傳入;
四、重復註解
在Java 5中使用註解有一個限制,即相同的註解在同一位置只能聲明一次。Java
8引入重復註解,這樣相同的註解在同一地方也可以聲明多次。重復註解機制本身需要用@Repeatable註解。Java
8在編譯器層做了優化,相同註解會以集合的方式保存,因此底層的原理並沒有變化。
五、擴展註解的支持
Java 8擴展了註解的上下文,幾乎可以為任何東西添加註解,包括局部變數、泛型類、父類與介面的實現,連方法的異常也能添加註解。
六、Optional
Java 8引入Optional類來防止空指針異常,Optional類最先是由Google的Guava項目引入的。Optional類實際上是個容器:它可以保存類型T的值,或者保存null。使用Optional類我們就不用顯式進行空指針檢查了。
七、Stream
Stream
API是把真正的函數式編程風格引入到Java中。其實簡單來說可以把Stream理解為MapRece,當然Google的MapRece的靈感也是來自函數式編程。她其實是一連串支持連續、並行聚集操作的元素。從語法上看,也很像linux的管道、或者鏈式編程,代碼寫起來簡潔明了,非常酷帥!
八、Date/Time API (JSR 310)
Java 8新的Date-Time API (JSR 310)受Joda-Time的影響,提供了新的java.time包,可以用來替代
java.util.Date和java.util.Calendar。一般會用到Clock、LocaleDate、LocalTime、LocaleDateTime、ZonedDateTime、Duration這些類,對於時間日期的改進還是非常不錯的。
九、JavaScript引擎Nashorn
Nashorn允許在JVM上開發運行JavaScript應用,允許Java與JavaScript相互調用。
十、Base64
在Java 8中,Base64編碼成為了Java類庫的標准。Base64類同時還提供了對URL、MIME友好的編碼器與解碼器。
除了這十大新特性之外,還有另外的一些新特性:
更好的類型推測機制:Java 8在類型推測方面有了很大的提高,這就使代碼更整潔,不需要太多的強制類型轉換了。
編譯器優化:Java 8將方法的參數名加入了位元組碼中,這樣在運行時通過反射就能獲取到參數名,只需要在編譯時使用-parameters參數。
並行(parallel)數組:支持對數組進行並行處理,主要是parallelSort()方法,它可以在多核機器上極大提高數組排序的速度。
並發(Concurrency):在新增Stream機制與Lambda的基礎之上,加入了一些新方法來支持聚集操作。
Nashorn引擎jjs:基於Nashorn引擎的命令行工具。它接受一些JavaScript源代碼為參數,並且執行這些源代碼。
類依賴分析器jdeps:可以顯示Java類的包級別或類級別的依賴。
JVM的PermGen空間被移除:取代它的是Metaspace(JEP 122)。
⑻ JDK8中增加的一個Stream介面,該介面可以將集合、數據中的元素轉換為Stream流對嗎
Java 8引入了全新的Stream API。這里的Stream和I/O流不同,它更像具有Iterable的集合類,但行為和集合類又有所不同。
Stream API引入的目的在於彌補Java函數式編程的缺陷。對於很多支持函數式編程的語言,map()、rece()基本上都內置到語言的標准庫中了,不過,Java 8的Stream API總體來講仍然是非常完善和強大,足以用很少的代碼完成許多復雜的功能。
創建一個Stream有很多方法,最簡單的方法是把一個Collection變成Stream。我們來看最基本的幾個操作:
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Stream<Integer> stream = numbers.stream();
stream.filter((x) -> {
return x % 2 == 0;
}).map((x) -> {
return x * x;
}).forEach(System.out::println);
}
集合類新增的stream()方法用於把一個集合變成Stream,然後,通過filter()、map()等實現Stream的變換。Stream還有一個forEach()來完成每個元素的迭代。
為什麼不在集合類實現這些操作,而是定義了全新的Stream API?Oracle官方給出了幾個重要原因:
一是集合類持有的所有元素都是存儲在內存中的,非常巨大的集合類會佔用大量的內存,而Stream的元素卻是在訪問的時候才被計算出來,這種「延遲計算」的特性有點類似Clojure的lazy-seq,佔用內存很少。
二是集合類的迭代邏輯是調用者負責,通常是for循環,而Stream的迭代是隱含在對Stream的各種操作中,例如map()。
要理解「延遲計算」,不妨創建一個無窮大小的Stream。
如果要表示自然數集合,顯然用集合類是不可能實現的,因為自然數有無窮多個。但是Stream可以做到。
自然數集合的規則非常簡單,每個元素都是前一個元素的值+1,因此,自然數發生器用代碼實現如下:
class NaturalSupplier implements Supplier<Long> {
long value = 0;
public Long get() {
this.value = this.value + 1;
return this.value;
}
}
反復調用get(),將得到一個無窮數列,利用這個Supplier,可以創建一個無窮的Stream:
public static void main(String[] args) {
Stream<Long> natural = Stream.generate(new NaturalSupplier());
natural.map((x) -> {
return x * x;
}).limit(10).forEach(System.out::println);
}
對這個Stream做任何map()、filter()等操作都是完全可以的,這說明Stream API對Stream進行轉換並生成一個新的Stream並非實時計算,而是做了延遲計算。
當然,對這個無窮的Stream不能直接調用forEach(),這樣會無限列印下去。但是我們可以利用limit()變換,把這個無窮Stream變換為有限的Stream。
利用Stream API,可以設計更加簡單的數據介面。例如,生成斐波那契數列,完全可以用一個無窮流表示(受限Java的long型大小,可以改為BigInteger):
class FibonacciSupplier implements Supplier<Long> {
long a = 0;
long b = 1;
@Override
public Long get() {
long x = a + b;
a = b;
b = x;
return a;
}
}
public class FibonacciStream {
public static void main(String[] args) {
Stream<Long> fibonacci = Stream.generate(new FibonacciSupplier());
fibonacci.limit(10).forEach(System.out::println);
}
}
如果想取得數列的前10項,用limit(10),如果想取得數列的第20~30項,用:
List<Long> list = fibonacci.skip(20).limit(10).collect(Collectors.toList());
最後通過collect()方法把Stream變為List。該List存儲的所有元素就已經是計算出的確定的元素了。
用Stream表示Fibonacci數列,其介面比任何其他介面定義都要來得簡單靈活並且高效。