JAVAの基礎-第16章スレッドプール、ラムダ式

メインコンテンツ

待機とウェイクアップのケース
スレッドプール
ラムダ式

教育目的

スレッド通信の概念を理解でき、Javaの動作原理でスレッドプールを記述できる
待機中のウェイクアップメカニズムを理解できます機能プログラミングに関して理解できますオブジェクト指向の利点標準フォーマットをマスターできますラムダ式はラムダランナブルを使用して標準フォーマットを使用でき、コンパレータインターフェイスはラムダをマスターできます式省略の形式とルールは、Lambda省略形式を使用でき、RunnableおよびComparatorインターフェイスを使用でき、Lambda標準形式を使用して(1つの抽象メソッドで)カスタムインターフェイスを使用でき、Lambda省略形式を使用してカスタムインターフェイスを使用できます(そして、唯一の抽象メソッドがあります)ラムダの2つの前提を明確にすることができます








第1章ウェイクアップメカニズムの待機

1.1スレッド間の通信

概念:複数のスレッドが同じリソースを処理していますが、処理アクション(スレッドのタスク)が異なります。

たとえば、スレッドAはパンを生成するために使用され、スレッドBはパンを食べるために使用されます。バンは同じリソースとして理解できます。スレッドAとスレッドBによって処理されるアクションは、生産用と消費用のアクションです。次に、スレッドAとスレッドBの間スレッド通信に問題があります。

スレッド間通信を扱う理由:

複数のスレッドが同時に実行される場合、CPUはデフォルトでスレッドをランダムに切り替えます。タスクを同時に完了するために複数のスレッドが必要で、それらを定期的に実行する場合、複数のスレッド間の調整と通信が必要です。データのマルチスレッド協調を実現するため。

スレッド間通信のためにリソースを効果的に使用する方法:

複数のスレッドが同じリソースと異なるタスクを処理している場合、スレッド間での同じ変数の使用または操作を解決するには、スレッド通信が必要です。つまり、複数のスレッドが同じデータを操作する場合、同じ共有変数の競合が回避されます。つまり、各スレッドがリソースを効果的に使用できるようにするために、特定の手段を使用する必要があります。そして、このメソッドはウェイクアップメカニズムを待っています。

1.2ウェイクアップメカニズムの待機

ウェイクアップメカニズムを待っているもの

これは、複数のスレッド間の協調メカニズムです。スレッドに関して言えば、ロックの競合など、スレッド間の競合を考えることがよくありますが、それだけではなく
、スレッド間の協調メカニズムも存在します。あなたや会社の同僚と同じように、昇進を競うことが
できますが、多くの場合、特定のタスクを完了するために協力します。

つまり、スレッドが所定の操作を実行した後、スレッドは待機状態に入り(wait())、他のスレッドが指定されたコードを実行するのを待ってから
起動します(notify());複数のスレッドが待機している場合、必要に応じて、notifyAll()を使用して、待機中のすべてのスレッドをウェイクアップできます。

待機/通知は、スレッド間の協調メカニズムです。

起きるのを待っています

ウェイクアップの待機メカニズムは、スレッド間の通信問題を解決するために使用されます。使用される3つの方法の意味は次のとおりです。

1. wait:线程不再活动,不再参与调度,进入 wait set 中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时
的线程状态即是 WAITING。它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象
上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中
2. notify:则选取所通知对象的 wait set 中的一个线程释放;例如,餐馆有空位置后,等候就餐最久的顾客最先
入座。
3. notifyAll:则释放所通知对象的 wait set 上的全部线程。
注意:
哪怕只通知了一个等待的线程,被通知线程也不能立即恢复执行,因为它当初中断的地方是在同步块内,而
此刻它已经不持有锁,所以她需要再次尝试去获取锁(很可能面临其它线程的竞争),成功后才能在当初调
用 wait 方法之后的地方恢复执行。
总结如下:
如果能获取锁,线程就从 WAITING 状态变成 RUNNABLE 状态;
否则,从 wait set 出来,又进入 entry set,线程就从 WAITING 状态又变成 BLOCKED 状态

waitメソッドとnotifyメソッドを呼び出すときに注意が必要な詳細

1. wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对
象调用的wait方法后的线程。
2. wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继
承了Object类的。
3. wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为:必须要通过锁对象调用这 2 个方
法。

1.3生産者と消費者の問題

1.3生産者と消費者の問題

ウェイクアップメカニズムを待つことは、実際には典型的な「プロデューサーおよびコンシューマー」の問題です。

一例として、まんじゅうの製造とまんじゅうの消費を例にとります。

コードデモ:

パンリソースクラス:

フードスレッドクラス:

バオジプスレッドクラス:

包子铺线程生产包子,吃货线程消费包子。当包子没有时(包子状态为false),吃货线程等待,包子铺线程生产包子
(即包子状态为true),并通知吃货线程(解除吃货的等待状态),因为已经有包子了,那么包子铺线程进入等待状态。
接下来,吃货线程能否进一步执行则取决于锁的获取情况。如果吃货获取到锁,那么就执行吃包子动作,包子吃完(包
子状态为false),并通知包子铺线程(解除包子铺的等待状态),吃货线程进入等待。包子铺线程能否进一步执行则取
决于锁的获取情况。
public class BaoZi {
String pier ;
String xianer ;
boolean flag = false ;//包子资源 是否存在 包子资源状态
}
public class ChiHuo extends Thread{
private BaoZi bz;
public ChiHuo(String name,BaoZi bz){
super(name);
this.bz = bz;
    }
@Override
public void run() {
while(true){
synchronized (bz){
if(bz.flag == false){//没包子
try {
bz.wait();
                    } catch (InterruptedException e) {
e.printStackTrace();
                    }
                }
System.out.println("吃货正在吃"+bz.pier+bz.xianer+"包子");
bz.flag = false;
bz.notify();
            }
        }
    }
}

テストカテゴリ:

public class BaoZiPu extends Thread {
private BaoZi bz;
public BaoZiPu(String name,BaoZi bz){
super(name);
this.bz = bz;
    }
@Override
public void run() {
int count =  0 ;
//造包子
while(true){
//同步
synchronized (bz){
if(bz.flag == true){//包子资源 存在
try {
bz.wait();
                    } catch (InterruptedException e) {
e.printStackTrace();
                    }
                }
// 没有包子 造包子
System.out.println("包子铺开始做包子");
if(count% 2  ==  0 ){
// 冰皮 五仁
bz.pier = "冰皮";
bz.xianer = "五仁";
                }else{
// 薄皮 牛肉大葱
bz.pier = "薄皮";
bz.xianer = "牛肉大葱";
                }
count++;
bz.flag=true;
System.out.println("包子造好了:"+bz.pier+bz.xianer);
System.out.println("吃货来吃吧");
//唤醒等待线程 (吃货)
bz.notify();
            }
        }
    }
}

実行効果:

第2章スレッドプール

2.1スレッドプールのアイデアの概要

public class Demo {
public static void main(String[] args) {
//等待唤醒案例
BaoZi bz = new BaoZi();
ChiHuo ch = new ChiHuo("吃货",bz);
BaoZiPu bzp = new BaoZiPu("包子铺",bz);
ch.start();
bzp.start();
    }
}

バオジプはパンを作り始めました

パンが作られました:氷の皮と5つのカーネル

来て、食べます

食通は氷皮の五連パンを食べています

バオジプはパンを作り始めました

パンが作られます:薄皮の牛肉とネギ

来て、食べます

食通は薄皮の牛肉とねぎまんを食べている

バオジプはパンを作り始めました

パンが作られました:氷の皮と5つのカーネル

来て、食べます

食通は氷皮の五連パンを食べています

スレッドを使用すると、スレッドが作成されます。これは実装が非常に簡単ですが、問題が発生します。

多数の同時スレッドがあり、各スレッドが短時間タスクを実行する場合、タスクは終了するため、スレッドの頻繁な作成が大幅に削減されます。

スレッドを頻繁に作成および破棄するのに時間がかかるため、システムの効率。

スレッドを再利用可能にする方法はありますか。つまり、タスクを実行した後、スレッドは破棄されませんが、他のタスクを実行し続けることができますか?

Javaでは、この効果はスレッドプールを介して実現できます。今日は、Javaスレッドプールについて詳しく説明します。

2.2スレッドプールの概念

スレッドプール:実際には、複数のスレッドを保持するコンテナーであり、スレッドを繰り返し使用できるため、スレッドオブジェクトを頻繁に作成する必要がありません。

スレッドを繰り返し作成し、リソースを大量に消費する必要はありません。

スレッドプールの多くの操作はリソースの最適化に関連しているため、ここでは詳しく説明しません。スレッドプールの動作原理を理解するために画像を使用します

理由:

スレッドプールを適切に使用すると、3つの利点があります。

1.リソース消費を減らします。スレッドの作成と破棄の回数が減り、各ワーカースレッドを再利用して複数のタスクを実行できます。

2.応答速度を改善します。タスクが到着すると、スレッドが作成されるまで待つことなく、タスクをすぐに実行できます。

3.スレッドの管理性を向上させます。スレッドプール内のワーカースレッドの数は、システムの手頃な価格に応じて調整して、過度の内部消費を防ぐことができます。

サーバーを保存してサーバーを停止します(各スレッドは約1MBのメモリを必要とし、より多くのスレッドが開かれ、より多くのメモリが消費され、最後にクラッシュします)。

2.3スレッドプールの使用

Javaのスレッドプールの最上位のインターフェイスはjava.util.concurrent.Executorですが、厳密には、Executorはスレッド
プールではなく、スレッドを実行するためのツールです。実際のスレッドプールインターフェイスはjava.util.concurrent.ExecutorServiceです。

スレッドプールの構成はより複雑であり、特にスレッドプールの原則があまり明確でない場合は、構成されたスレッドプールが最適
はない可能性が高いため、java.util.concurrent.Executorsスレッドファクトリクラスで提供されます。一部の静的ファクトリーは、いくつかの一般的に使用されるスレッドプールを生成するために作成されます。公式な
推奨事項は、Executorsエンジニアリングクラスを使用してスレッドプールオブジェクトを作成することです。

次のように、Executorsクラスにスレッドプールを作成するメソッドがあります。

public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象。(创建的是有界线
程池,也就是池中的线程个数可以指定最大数量)

スレッドプールのExecutorServiceオブジェクトを取得し、それを使用する方法を次に示します。スレッドプールオブジェクトを使用するメソッドは次のように定義されています。

public Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行
Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用。

スレッドプールでスレッドオブジェクトを使用する手順:

1. 创建线程池对象。
2. 创建Runnable接口子类对象。(task)
3. 提交Runnable接口子类对象。(take task)
4. 关闭线程池(一般不做)。

実行可能な実装クラスコード:

スレッドプールテストクラス:

第3章ラムダ式

3.1関数型プログラミングのアイデアの概要

public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("我要一个教练");
try {
Thread.sleep( 2000 );
        } catch (InterruptedException e) {
e.printStackTrace();
        }
System.out.println("教练来了: " + Thread.currentThread().getName());
System.out.println("教我游泳,交完后,教练回到了游泳池");
    }
}
public class ThreadPoolDemo {
public static void main(String[] args) {
// 创建线程池对象
ExecutorService service = Executors.newFixedThreadPool( 2 );//包含 2 个线程对象
// 创建Runnable实例对象
MyRunnable r = new MyRunnable();
//自己创建线程对象的方式
// Thread t = new Thread(r);
// t.start(); ‐‐‐> 调用MyRunnable中的run()
// 从线程池中获取线程对象,然后调用MyRunnable中的run()
service.submit(r);
// 再获取个线程对象,调用MyRunnable中的run()
service.submit(r);
service.submit(r);
// 注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。
// 将使用完的线程又归还到了线程池中
// 关闭线程池
//service.shutdown();
    }
}

数学では、関数は入力と出力、つまり「何かで何かをする」を伴う計算スキームのセットです。比較的言えば、オブジェクト指向

ポイントは「オブジェクトの形で物事をしなければならない」ことを強調し、機能的思考はオブジェクト指向の複雑な構文を無視しようとしますが、代わりに何をすべきかを強調します

何をするか。

オブジェクト指向の考え方:

1つのことを行って、問題を解決できるオブジェクトを見つけ、オブジェクトのメソッドを呼び出し、そのことを完了します。

関数型プログラミングのアイデア:

結果が得られる限り、誰がどのようにしたかは関係ありません。誰が行ったかは関係ありません。プロセスではなく結果に注意を払います

3.2冗長実行可能コード

伝統的な書物

タスクを完了するためにスレッドを開始する必要がある場合、通常、タスクの内容はjava.lang.Runnableインターフェースを介して定義され、
java.lang.Threadクラスを使用してスレッドが開始されます。コードは以下のように表示されます:

「すべてがオブジェクトである」という考えに基づいて、このアプローチは理解できます。最初にRunnableインターフェースの匿名の内部クラスオブジェクトを作成してタスクの
内容を指定し、次にそれをスレッドに渡して開始します。

コード分​​析

Runnableの匿名内部クラスを使用する場合、いくつかのポイントを分析できます。

Thread类需要Runnable接口作为参数,其中的抽象run方法是用来指定线程任务内容的核心;
public class Demo01Runnable {
public static void main(String[] args) {
// 匿名内部类
Runnable task = new Runnable() {
@Override
public void run() { // 覆盖重写抽象方法
System.out.println("多线程任务执行!");
}
};
new Thread(task).start(); // 启动线程
}
}
为了指定run的方法体,不得不需要Runnable接口的实现类;
为了省去定义一个RunnableImpl实现类的麻烦,不得不使用匿名内部类;
必须覆盖重写抽象run方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;
而实际上,似乎只有方法体才是关键所在。

3.3プログラミングのアイデアの変換

方法ではなく、何をすべきか

本当に匿名の内部クラスオブジェクトを作成しますか?しない。これを行うには、オブジェクトを作成する必要があります。本当にやりたい

問題は、実行メソッド本体のコードをThreadクラスに渡して知ることです。

コードを渡します。これが私たちの真の目的です。オブジェクトの作成は、オブジェクト指向構文の制限のために採用する必要がある方法にすぎません。
それで、もっと簡単な方法はありますか?「方法」から「何をすべきか」の本質に焦点を戻すと、
目標をよりよく達成できる限り、プロセスと形式はそれほど重要ではないことがわかります

人生の例

北京から上海に移動する必要がある場合は、高速鉄道、自動車、サイクリング、ウォーキングを選択できます。私たちの本当の目的は上海に到達することですが、どうやってそこに着くことができますか

上海の形式は重要ではないので、高速鉄道の飛行機よりも良い方法があるかどうかを調査してきました。

そして今、この種の飛行機(または宇宙船)が誕生しました。オラクルが2014年3月にリリースしたJava 8(JDK 1.8)
、ラムダ式の重量級の新機能を追加し、新しい世界への扉を開きました。

3.4 Lambdaの優れた記述を体験する

Java 8の新しい構文の助けを借りて、上記のRunnableインターフェースの匿名の内部クラスの記述は、より単純なLambda式を通じて同等にすることができます。

このコードは、現在の実行効果とまったく同じであり、1.8以上のコンパイルレベルで渡すことができます。これは、コードのセマンティクスから見ることができます。

スレッドが開始され、スレッドタスクの内容がより簡潔な形式で指定されます。

「インターフェイスオブジェクトを作成する必要がある」という制限はなくなり、「抽象メソッドの上書きと再書き込み」の負担もなくなりました。

3.5匿名内部クラスのレビュー

Lambdaはどのようにオブジェクト指向を打ち負かしていますか?上記の例では、コアコードは実際には次のとおりです。

Lambdaのセマンティクスを理解するには、従来のコードから始める必要があります。

実装クラスを使用

スレッドを開始するには、Threadクラスのオブジェクトを作成し、startメソッドを呼び出す必要があります。スレッド実行の内容を指定するに
は、Threadクラスのコンストラクターを呼び出す必要があります

public Thread(Runnable target)

Runnableインターフェースの実装オブジェクトを取得するために、実装クラスRunnableImplをインターフェースに定義できます。

次に、実装クラスのオブジェクトをThreadクラスの構築パラメーターとして作成します。

匿名の内部クラスを使用する

このRunnableImplクラスは、Runnableインターフェースを実装するためにのみ存在し、一度だけ使用されるため、匿名内部クラス
構文を使用すると、このクラスの個別の定義、つまり匿名内部クラスを保存できます。

public class Demo02LambdaRunnable {
public static void main(String[] args) {
new Thread(() ‐> System.out.println("多线程任务执行!")).start(); // 启动线程
}
}
() ‐> System.out.println("多线程任务执行!")
public class RunnableImpl implements Runnable {
@Override
public void run() {
System.out.println("多线程任务执行!");
}
}
public class Demo03ThreadInitParam {
public static void main(String[] args) {
Runnable task = new RunnableImpl();
new Thread(task).start();
}
}

匿名内部クラスの長所と短所

匿名の内部クラスは、実装クラスの定義を保存するのに役立ちますが、匿名の内部クラスの構文は非常に複雑です。

セマンティック分析

コードのセマンティクスを注意深く分析すると、Runnableインターフェースにはrunメソッドの定義が1つしかありません。

public abstract void run();

つまり、物事(実際には機能)を実行するための計画が策定されます。

无参数:不需要任何条件即可执行该方案。
无返回值:该方案不产生任何结果。
代码块(方法体):该方案的具体执行步骤。

同じセマンティクスがLambda構文にも反映されています。

前面的一对小括号即run方法的参数(无),代表不需要任何条件;
中间的一个箭头代表将前面的参数传递给后面的代码;
后面的输出语句即业务逻辑代码。

3.6ラムダ標準形式

Lambdaはオブジェクト指向の規則と規制を排除し、形式は3つの部分で構成されます。

一些参数
一个箭头
一段代码

ラムダ式の標準形式は次のとおりです。

フォーマットの説明:

括弧内の構文は、従来のメソッドパラメータリストと一致しています。パラメータがない場合は空白のままにし、複数のパラメータはカンマで区切ります。

->は、新たに導入された文法フォーマットであり、ポインティングアクションを表しています。

中括弧内の文法は、基本的には従来の方法と同じです。

3.7演習:Lambda標準形式を使用(パラメーターなし、戻りなし)

public class Demo04ThreadNameless {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("多线程任务执行!");
}
}).start();
}
}
() ‐> System.out.println("多线程任务执行!")

(パラメータータイプパラメーター名)-> {コードステートメント}

3.7演習:Lambda標準形式を使用(パラメーターなし、戻りなし)

トピック

クックインターフェースが指定された場合、これには唯一の抽象メソッドmakeFoodが含まれ、パラメーターも戻り値もありません。次のように:

次のコードでは、Lambdaの標準形式を使用して、invokeCookメソッドを呼び出し、「Eating!」という単語を出力してください。

回答

备注:小括号代表Cook接口makeFood抽象方法的参数为空,大括号代表makeFood的方法体。

3.8 Lambdaパラメータと戻り値

次の例は、抽象メソッドが次のように定義されているjava.util.Comparatorインターフェースの使用シナリオコードを示しています。

public abstract int compare(T o1, T o2);

オブジェクトの配列をソートする必要がある場合、Arrays.sortメソッドには、ソートルールを指定するためのComparatorインターフェースのインスタンスが必要です。そこにあるとし
た文字列名とint型年齢:2つのメンバ変数を持つPersonクラスは:

public interface Cook {
void makeFood();
}
public class Demo05InvokeCook {
public static void main(String[] args) {
// TODO 请在此使用Lambda【标准格式】调用invokeCook方法
    }
private static void invokeCook(Cook cook) {
cook.makeFood();
    }
}
public static void main(String[] args) {
invokeCook(() ‐> {
System.out.println("吃饭啦!");
    });
}

要求する:

使用数组存储多个Person对象
对数组中的Person对象使用Arrays的sort方法通过年龄进行升序排序

伝統的な書物

従来のコードを使用してPerson []配列をソートする場合、書き込み方法は次のとおりです。

このアプローチは、オブジェクト指向の考え方では「当然のこと」のようです。(匿名の内部クラスを使用した)コンパレーターインターフェースのインスタンスは、
「最年少から最古へ」のソートルールを表します。

コード分​​析

上記のコードが実際に何をしているのかを見てみましょう。

为了排序,Arrays.sort方法需要排序规则,即Comparator接口的实例,抽象方法compare是关键;
为了指定compare的方法体,不得不需要Comparator接口的实现类;
为了省去定义一个ComparatorImpl实现类的麻烦,不得不使用匿名内部类;
必须覆盖重写抽象compare方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;
实际上,只有参数和方法体才是关键。

ラムダライティング

public class Person {
private String name;
private int age;
// 省略构造器、toString方法与Getter Setter
}
import java.util.Arrays;
import java.util.Comparator;
public class Demo06Comparator {
public static void main(String[] args) {
// 本来年龄乱序的对象数组
Person[] array = {
new Person("古力娜扎",  19 ),
new Person("迪丽热巴",  18 ),
new Person("马尔扎哈",  20 ) };
// 匿名内部类
Comparator<Person> comp = new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() ‐ o2.getAge();
            }
        };
Arrays.sort(array, comp); // 第二个参数为排序规则,即Comparator接口实例
for (Person person : array) {
System.out.println(person);
        }
    }
}

3.9演習:Lambda標準形式を使用する(参加および復帰)

トピック

Calculatorインターフェイスを指定すると、2つのint数を加算して合計を取得する抽象メソッドcalcが含まれます。

次のコードでは、Lambdaの標準形式を使用して、invokeCalcメソッドを呼び出し、120と130の加算計算を完了してください。

回答

import java.util.Arrays;
public class Demo07ComparatorLambda {
public static void main(String[] args) {
Person[] array = {
new Person("古力娜扎",  19 ),
new Person("迪丽热巴",  18 ),
new Person("马尔扎哈",  20 ) };
Arrays.sort(array, (Person a, Person b) ‐> {
return a.getAge() ‐ b.getAge();
        });
for (Person person : array) {
System.out.println(person);
        }
    }
}
public interface Calculator {
int calc(int a, int b);
}
public class Demo08InvokeCalc {
public static void main(String[] args) {
// TODO 请在此使用Lambda【标准格式】调用invokeCalc方法来计算120+130的结果ß
    }
private static void invokeCalc(int a, int b, Calculator calculator) {
int result = calculator.calc(a, b);
System.out.println("结果是:" + result);
    }
}
public static void main(String[] args) {
invokeCalc( 120 ,  130 , (int a, int b) ‐> {
return a + b;
    });
}
备注:小括号代表Calculator接口calc抽象方法的参数,大括号代表calc的方法体。

3.10ラムダ省略形式

導出または省略可能

ラムダは「方法」ではなく「何」を強調するので、コンテキストから派生できる情報は省略できます。たとえば、上記の例
ではLambdaを省略して使用することもできます。

省略ルール

Lambda標準形式に基づくと、省略を使用するための規則は次のとおりです。

1. 小括号内参数的类型可以省略;
2. 如果小括号内有且仅有一个参,则小括号可以省略;
3. 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
备注:掌握这些省略规则后,请对应地回顾本章开头的多线程案例。

3.11演習:Lambdaを使用してフォーマットを省略する

トピック

makeFoodの唯一の抽象メソッドを含む以前のクックインターフェイスを引き続き使用して、次のコードでは、Lambdaの省略された形式を使用して、
invokeCookメソッドを呼び出し、「Eating!」という単語を出力してください。

回答

3.12 Lambdaを使用するための前提条件

public static void main(String[] args) {
invokeCalc( 120 ,  130 , (a, b) ‐> a + b);
}
public class Demo09InvokeCook {
public static void main(String[] args) {
// TODO 请在此使用Lambda【省略格式】调用invokeCook方法
    }
private static void invokeCook(Cook cook) {
cook.makeFood();
    }
}
public static void main(String[] args) {
invokeCook(() ‐> System.out.println("吃饭啦!"));
}

Lambdaの構文は非常に簡潔で、オブジェクト指向の複雑さの制約がありません。ただし、使用するときに特別な注意が必要ないくつかの問題があります。

1. 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。
无论是JDK内置的Runnable、Comparator接口还是自定义的接口,只有当接口中的抽象方法存在且唯一
时,才可以使用Lambda。
2. 使用Lambda必须具有上下文推断。
也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。
备注:有且仅有一个抽象方法的接口,称为“函数式接口”。

おすすめ

転載: blog.csdn.net/weixin_43419256/article/details/108230718