「ジャワはまだ死んでいない。人々はそれを理解し始めている。」
このチュートリアルでは、シンプルな注釈付きコードを使用して新機能を説明します。これは、大ヒテキストが表示されません。
1.インターフェイスのデフォルトメソッド
Java 8を使用すると、インターフェイスにアブストラクトメソッドの実装を追加できます。この機能は、次のようなものです。
コードコピーは次のとおりです。
インターフェイスフォーミュラ{
二重計算(int a);
デフォルトのdouble sqrt(int a){
return math.sqrt(a);
}
}
計算されたメソッドを使用することに加えて、式インターフェイスは、式インターフェイスを実装するSQRTメソッドも定義します。
コードコピーは次のとおりです。
formula formula = new formula(){
@オーバーライド
パブリックダブル計算(int a){
sqrt(a * 100)を返します。
}
};
formula.calculate(100);
formula.sqrt(16);
この記事の式は、匿名クラスのインスタンスとして実装されています。次のセクションでは、シングルメソッドインターフェイスを実装するためのより簡単なアプローチが表示されます。
翻訳者のメモ:Javaには単一の継承しかありません。通常、インターフェイスを使用してC ++でサポートされます。他の言語では、クラスを持つ方法は、同時に他の再利用可能なコードを持つ方法はMixinと呼ばれます。この新しいJava 8 Specialは、コンパイラの実装の観点からのScalaの特性に近いものです。 C#には拡張法と呼ばれる概念もあります。これにより、既存のタイプを拡張できます。これは、Java 8のこれと意味的に異なります。
2。ラムダ式
まず、Javaの古いバージョンで文字列がどのように配置されるかを見てみましょう。
コードコピーは次のとおりです。
リスト<文字列> names = arrays.aslist( "peter"、 "anna"、 "mike"、 "xenia");
collections.sort(names、new Comparator <String>(){
@オーバーライド
public int Compare(文字列A、文字列B){
b.compareto(a)を返します。
}
});
リストオブジェクトとコンパレータを静的メソッドコレクションに渡すだけです。通常、匿名のComparatorオブジェクトを作成し、ソートメソッドに渡すために行われます。
Java 8では、この従来の匿名オブジェクト8を使用する必要はありません。
コードコピーは次のとおりです。
collections.sort(names、(string a、string b) - > {
b.compareto(a)を返します。
});
参照してください、コードはよりセグメント化され、読み取り可能になりますが、実際には短く書くことができます:
コードコピーは次のとおりです。
collections.sort(names、(string a、string b) - > b.compareto(a));
コードの1行しかない関数本文の場合、ブレース{}を削除してキーワードを返すことができますが、より短く書くことができます。
コードコピーは次のとおりです。
collections.sort(names、(a、b) - > b.compareto(a));
Javaコンパイラはパラメータータイプを自動的に推定できるため、タイプを再度書き込む必要はありません。次に、もっと便利なラムダ式ができることを見てみましょう。
3。機能インターフェイス
ラムダの式は、Javaのタイプシステムでどのように表されますか?各ラムダ式は、通常はインターフェイスタイプであるタイプに対応します。 「機能的インターフェイス」とは、抽象的なメソッドのみを含むインターフェイスを指し、このタイプの各ラムダ式はこの抽象的なメソッドと一致します。デフォルトの方法は抽象的なメソッドではないため、機能的なインターフェイスにデフォルトのメソッドを追加することもできます。
Lambda式は、インターフェイスがこの要件を満たす必要があることを確認するために、1つの抽象的な方法を含む任意のインターフェイスタイプとして扱うことができます。注釈では、マークしたインターフェイスがこの注釈によって注釈が付けられていることがわかります。
例は次のとおりです。
コードコピーは次のとおりです。
@functionalinterface
インターフェイスコンバーター<f、t> {
t convert(f from);
}
converter <string、integer> converter =(from) - > integer.valueof(from);
整数変換= converter.convert( "123");
System.out.println(変換);
@functionalinterfaceが指定されていない場合、上記のコードも正しいことに注意する必要があります。
翻訳者のメモは、ラミソッドインターフェースに式をマッピングします。関数は、典型的なアプリケーションのシナリオを自動的に作成します。
4。メソッドおよびコンストラクターの参照
前のセクションのコードは、静的メソッド参照で表すこともできます。
コードコピーは次のとおりです。
converter <string、integer> converter = integer :: valueof;
整数変換= converter.convert( "123");
System.out.println(変換);
Java 8では、::キーワードを使用してメソッドまたはコンストラクターの参照を示します。
コードコピーは次のとおりです。
converter = monthy :: startswith;
string converted = converter.convert( "java");
System.out.println(変換);
::キーワードを使用して、コンストラクターがどのように参照されるかを見てみましょう。
コードコピーは次のとおりです。
クラスの人{
文字列firstName;
文字列lastName;
人() {}
Person(string firstName、string lastName){
this.firstname = firstName;
this.lastname = lastname;
}
}
次に、オブジェクトファクトリーインターフェイスを指定して、人オブジェクトを作成します。
コードコピーは次のとおりです。
Interface PersonFactory <P extends person> {
P create(string firstName、string lastName);
}
ここでは、完全な工場を実装する代わりに、コンストラクターの参照を使用してそれらを関連付けます。
コードコピーは次のとおりです。
PersonFactory <Person> PersonFactory = Person :: new;
Person person = personfactory.create( "Peter"、 "Parker");
Person Class Constructorへの参照を取得するには、個人::新品のみが必要です。
5。ラムダの範囲
ラムダ式の外側のスコープにアクセスする方法は、匿名オブジェクトの古いバージョンの外側のスコープと似ています。マークされた最終的な外部ローカル変数、またはインスタンスのフィールドと静的変数に直接アクセスできます。
6.ローカル変数にアクセスします
Lambda式で外部のローカル変数に直接アクセスできます。
コードコピーは次のとおりです。
final int num = 1;
converter <integer、string> stringconverter =
(from) - > string.valueof(from + num);
StringConverter.Convert(2);
ただし、匿名のオブジェクトとは異なり、ここでの変数番号は、最終と宣言することなく最終的に宣言できます。コードも正しいです。
コードコピーは次のとおりです。
int num = 1;
converter <integer、string> stringconverter =
(from) - > string.valueof(from + num);
StringConverter.Convert(2);
ただし、ここでのNumは、後続のコード(つまり、暗黙的に最終的なセマンティクスがある)によって変更されてはなりません。たとえば、以下はコンパイルできません。
コードコピーは次のとおりです。
int num = 1;
converter <integer、string> stringconverter =
(from) - > string.valueof(from + num);
num = 3;
Lambda式でnumを変更しようとすることも許可されていません。
7。オブジェクトフィールドと静的変数にアクセスします
ローカル変数とは異なり、ラムダ内のフィールドと静的変数を読み書きできます。この動作は、匿名のオブジェクトと一致しています。
次のようにコードをコピーします:class lambda4 {
静的int outerstaticnum;
int outernum;
void testScopes(){
converter <integer、string> stringconverter1 =(from) - > {
outernum = 23;
return string.valueof(from);
};
converter <integer、string> stringconverter2 =(from) - > {
outerstaticnum = 72;
return string.valueof(from);
};
}
}
8。インターフェイスにアクセスするデフォルトメソッド
最初のセクションの式の例は、匿名オブジェクトを含むフォーミュラインスタンスで直接アクセスできるデフォルトのメソッドSQRTを定義しますが、これはLambda式では機能しません。
デフォルトの方法にはLambda式ではアクセスできず、次のコードはコンパイルされません。
コードコピーは次のとおりです。
式式=(a) - > sqrt(a * 100);
組み込み機能インターフェイス
JDK 1.8 APIには、古いJavaで一般的に使用される比較や実行可能なインターフェイスなど、多くの組み込みの機能インターフェイスが含まれており、これらのインターフェイスには、ラムダスで使用される@FunctionalInterfaceアノテーションが追加されています。
Java 8 APIは、作業をより便利にするための多くの機能的なインターフェースも提供します。
述語インターフェイス
Predicateインターフェイスには、ブールタイプを返すパラメーターが1つしかありません。このインターフェイスには、述語を他の複雑なロジックに組み合わせる多数のデフォルトメソッドが含まれています(vers、または、nonなど):
コードコピーは次のとおりです。
述語<string> predicate =(s) - > s.length()> 0;
predict.test( "foo");
predict.negate()。test( "foo");
Predicate <Boolean> nonnull = objects :: nonnull;
Predicate <Boolean> isnull = objects :: isnull;
Predicate <string> isempty = string :: isempty;
Predicate <string> isnotempty = isempty.negate();
関数インターフェイス
関数インターフェイスにはパラメーターがあり、結果が返され、他の関数と組み合わせることができるデフォルトのメソッド(構成、その後)が付属しています。
コードコピーは次のとおりです。
function <string、integer> tointeger = integer :: valueof;
function <string、string> backtostring = tointeger.andthen(string :: valueof);
backtostring.apply( "123");
サプライヤーインターフェイス
サプライヤーインターフェイスは、任意のタイプの値を返します。インターフェイスにはパラメーターがありません。
コードコピーは次のとおりです。
supplier <person> personsupplier = person :: new;
personsupplier.get();
消費者インターフェイス
消費者インターフェイスは、単一のパラメーターで実行される操作を表します。
コードコピーは次のとおりです。
Consumer <Person> Greeter =(P) - > System.out.println( "Hello、" + P.FirstName);
Greeter.Accept(新しい人( "Luke"、 "Skywalker"));
コンパレータインターフェイス
ComparatorはOld Javaの古典的なインターフェイスであり、Java 8はさまざまなデフォルトメソッドを追加しました。
コードコピーは次のとおりです。
Comparator <Person> Comparator =(P1、P2) - > p1.firstname.compareto(p2.firstname);
人P1 =新しい人( "John"、 "doe");
人P2 =新しい人(「アリス」、「ワンダーランド」);
Comparator.comPare(P1、P2);
Comparator.Reversed()。比較(P1、P2);
オプションのインターフェイス
オプションは、nullpointerexception例外を防ぐために使用される補助タイプです。
オプションは、値がヌルであるかどうかにかかわらず、単純なコンテナとして定義されます。 Java 8の前に、関数は通常、空のオブジェクトを返しますが、Java 8では、nullを返すことは推奨されません。
コードコピーは次のとおりです。
オプション<string> optional = optional.of( "bam");
optional.ispresent();
optional.get();
optional.orelse( "fallback");
optional.ifpresent((s) - > system.out.println(s.charat(0)));
ストリームインターフェイス
Java.util.Streamは、一度に一連の要素に適用できる一連の操作を表します。ストリーム操作は2つのタイプに分かれています。中間操作または最終操作は、特定のタイプの計算結果を返し、中間操作はストリーム自体を返し、複数の操作を順番に編成できます。ストリームの作成には、MAPでサポートされていないjava.util.collection、list、またはsetのサブクラスなど、データソースを指定する必要があります。ストリーム操作は、シリアルまたは並行して実行できます。
最初に、最初にどのように使用されるかを見てみましょう。
コードコピーは次のとおりです。
List <String> StringCollection = new ArrayList <>();
stringcollection.add( "ddd2");
stringcollection.add( "aaa2");
stringcollection.add( "bbb1");
stringcollection.add( "aaa1");
stringcollection.add( "bbb3");
stringcollection.add( "ccc");
stringcollection.add( "bbb2");
stringcollection.add( "ddd1");
Java 8はコレクションクラスを拡張し、collection.stream()またはcollection.parallelstream()を介してストリームを作成できます。次のセクションでは、一般的に使用されるストリーム操作について詳しく説明します。
フィルターフィルター
フィルタリングは述語インターフェースを介してフィルタリングされ、基準を満たす要素のみが中間操作であるため、フィルタリングされた結果に他のストリーム操作(Foreachなど)を適用できます。 foreachには、順番にフィルタリングされた要素を実行する関数が必要です。 Foreachは最終的な操作であるため、Foreach後に他のストリーム操作を実行することはできません。
コードコピーは次のとおりです。
StringCollection
。ストリーム()
.filter((s) - > S.Startswith( "A"))
.foreach(System.out :: println);
// "aaa2"、 "aaa1"
ソートソート
ソートは中間操作であり、ソート後の返されたストリームが返されます。カスタムコンパレータを指定しない場合、デフォルトのソートが使用されます。
コードコピーは次のとおりです。
StringCollection
。ストリーム()
.sorted()
.filter((s) - > S.Startswith( "A"))
.foreach(System.out :: println);
//「AAA1」、「AAA2」
ソートはアレンジされたストリームのみを作成し、元のデータソースに影響しないことに注意してください。
コードコピーは次のとおりです。
System.out.println(StringCollection);
// DDD2、AAA2、BBB1、AAA1、BBB3、CCC、BBB2、DDD1
マップマッピング
中間操作マップは、指定された関数インターフェイスに従って、要素を順番に追加のオブジェクトに変換します。 MAPを使用して、オブジェクトを他のタイプに変換することもできます。
コードコピーは次のとおりです。
StringCollection
。ストリーム()
.map(string :: touppercase)
.sorted((a、b) - > b.compareto(a))
.foreach(System.out :: println);
// "ddd2"、 "ddd1"、 "ccc"、 "bbb3"、 "bbb2"、 "aaa2"、 "aaa1"
マッチ
Streamはさまざまな一致操作を提供し、指定された述語がストリーム全体と一致するかどうかを検出できます。すべてのマッチング操作は最終操作であり、タイプブールの値を返します。
コードコピーは次のとおりです。
boolean anystartswitha =
StringCollection
。ストリーム()
.anymatch((s) - > s.startswith( "a"));
System.out.println(anystartswitha);
boolean allstartswitha =
StringCollection
。ストリーム()
.allMatch((s) - > S.Startswith( "a"));
system.out.println(allstartswitha);
Boolean nonstartswithz =
StringCollection
。ストリーム()
.nonematch((s) - > s.startswith( "z"));
System.out.println(nonestartswithz);
カウント
カウントは最終操作であり、ストリーム内の要素の数を返し、返品値の種類は長いです。
コードコピーは次のとおりです。
long startswithb =
StringCollection
。ストリーム()
.filter((s) - > S.Startswith( "B"))
。カウント();
System.out.println(startswithb);
規制を削減します
これは最終操作であり、ストリーム内の複数の要素を指定された関数を介して1つの要素として定義できるようにします。適格性の結果は、オプションのインターフェイスで表されます。
コードコピーは次のとおりです。
オプション<string> reduction =
StringCollection
。ストリーム()
.sorted()
.reduce((s1、s2) - > s1 + "#" + s2);
reduced.ifpresent(system.out :: println);
// "aaa1#aaa2#bb1#bb2#bb3#ccc#ddd1#ddd2"
平行ストリーム
前述のように、ストリームにはシリアルと並列の2つのタイプがあります。
次の例は、並列ストリームを介してパフォーマンスを改善する方法を示しています。
最初に、要素を重複せずに大きなテーブルを作成します。
コードコピーは次のとおりです。
int max = 1000000;
リスト<文字列>値= new ArrayList <>(max);
for(int i = 0; i <max; i ++){
uuid uuid = uuid.randomuuid();
values.add(uuid.tostring());
}
次に、このストリームを並べ替えるのに時間がかかる時間を計算します。
シリアルソート:
コードコピーは次のとおりです。
long t0 = system.nanotime();
long count = values.stream()。sorted()。count();
System.out.println(count);
long t1 = system.nanotime();
long millis = timeunit.nanoseconds.tomillis(t1 -t0);
system.out.println(string.format( "sequential sort take:%d ms"、millis));
//シリアル時間:899ミリ秒
並列ソート:
コードコピーは次のとおりです。
long t0 = system.nanotime();
long count = values.parallelstream()。sorted()。count();
System.out.println(count);
long t1 = system.nanotime();
long millis = timeunit.nanoseconds.tomillis(t1 -t0);
system.out.println(string.format( "parallel sort take:%d ms"、millis));
//パラレルソートには時間がかかります:472 ms
上記の2つのコードはほぼ同じですが、並列バージョンは50%と同じくらい高速です。
地図
前述のように、マップタイプはストリームをサポートしていませんが、マップは、いくつかの毎日のタスクを処理するためのいくつかの新しい便利な方法を提供します。
コードコピーは次のとおりです。
map <integer、string> map = new Hashmap <>();
for(int i = 0; i <10; i ++){
map.putifabsent(i、 "val" + i);
}
map.foreach((id、val) - > system.out.println(val));
上記のコードは理解しやすいです。
次の例は、地図上の他の有用な機能を示しています。
コードコピーは次のとおりです。
map.computeifpresent(3、(num、val) - > val + num);
map.get(3);
map.computeifpresent(9、(num、val) - > null);
map.containskey(9);
map.comPuteifabsent(23、num-> "val" + num);
map.containskey(23);
map.computeifabsent(3、num-> "bam");
map.get(3);
次に、すべてのキーに一致するマップ内のアイテムを削除する方法を示します。
コードコピーは次のとおりです。
map.remove(3、 "val3");
map.get(3);
map.remove(3、 "val33");
map.get(3);
別の便利な方法:
コードコピーは次のとおりです。
map.getordefault(42、 "Not not");
また、マップの要素を簡単にマージすることもできます。
コードコピーは次のとおりです。
map.merge(9、 "val9"、(value、newValue) - > value.concat(newValue));
map.get(9);
map.merge(9、 "concat"、(value、newValue) - > value.concat(newValue));
map.get(9);
マージが行うのは、キー名が存在しない場合に挿入することです。そうでない場合は、元のキーに対応する値をマージし、マップに再挿入します。
9。日付API
Java 8には、パッケージJava.timeの下に新しい時間と日付のAPIの新しいセットが含まれています。新しい日付APIは、オープンソースのJoda-Timeライブラリに似ていますが、この新しいAPIの最も重要な部分のいくつかを示しています。
クロッククロック
クロッククラスは、現在の日付と時刻にアクセスする方法を提供します。特定の時点は、インスタントクラスで表現することもできます。インスタントクラスは、古いjava.util.dateオブジェクトを作成するためにも使用できます。
コードコピーは次のとおりです。
clock clock = clock.systemdefaultzone();
long millis = clock.millis();
instant instant = clock.instant();
date legacydate = date.from(instant);
タイムゾーンタイムゾーン
新しいAPIでは、タイムゾーンはZoneIDで表されます。タイムゾーンは、の静的方法を使用して簡単に取得できます。 タイムゾーンは、UTS時間の時差を定義します。これは、インスタントタイムポイントオブジェクトとローカルナツメヤシオブジェクトを変換するときに非常に重要です。
コードコピーは次のとおりです。
system.out.println(ZONEID.GETAVAILZONEIDS());
//利用可能なすべてのタイムゾーンIDを印刷します
ZONEID ZONE1 = ZONEID.OF( "Europe/Berlin");
ZONEID ZONE2 = ZONEID.OF( "Brazil/East");
system.out.println(Zone1.getRules());
system.out.println(Zone2.getRules());
// Zonerules [currentStandardOffset =+01:00]
// Zonerules [currentStandardOffset = -03:00]
現地の現地時間
LocalTimeは、午後10時、17:30:15などのタイムゾーン情報のない時間を定義します。次の例では、前のコードによって作成されたタイムゾーンを使用して2つのローカル時間を作成します。その後、時間を比較し、2回の時間差は時間と分で計算されます。
コードコピーは次のとおりです。
localtime now1 = localtime.now(ゾーン1);
localtime now2 = localtime.now(ゾーン2);
system.out.println(now1.isbefore(now2));
長時間= Chronounit.hours.between(now1、now2);
長い時間= chronounit.minutes.between(now1、now2);
system.out.println(hoursbetween);
system.out.println(minutes -wiene);
LocalTimeは、解析タイム文字列を含むオブジェクトの作成を簡素化するためのさまざまな工場の方法を提供します。
コードコピーは次のとおりです。
localtime late = localtime.of(23、59、59);
system.out.println(後期);
DateTimeFormatter dirgenformatter =
DateTimeFormatter
.oflocalizedtime(formatstyle.short)
.withlocale(locale.german);
localtime leettime = localtime.parse( "13:37"、germanformatter);
System.out.println(leettime);
ローカルデートローカルデート
LocalDateは、2014-03-11などの正確な日付を表します。このオブジェクトの値は不変であり、基本的にLocalTimeの値と同じです。次の例は、日付オブジェクトに/年/年を追加する方法を示しています。また、これらのオブジェクトは不変であり、操作は新しいインスタンスを返すことに注意してください。
コードコピーは次のとおりです。
LocalDate Today = localdate.now();
LocalDate Tomorrow = Today.Plus(1、Chronounit.days);
昨日localdate =明日。マイナスデイ(2);
LocalDate Independenceday = localdate.of(2014、month.july、4);
Dayofweek dayofweek = Independenceday.getDayofweek();
System.out.println(dayofweek);
文字列からローカルデートタイプを解析することは、ローカルタイムを解析するのと同じくらい簡単です。
コードコピーは次のとおりです。
DateTimeFormatter dirgenformatter =
DateTimeFormatter
.oflocalizeddate(formatstyle.medium)
.withlocale(locale.german);
localdate xmas = localdate.parse( "24.12.2014"、germanformatter);
System.out.println(xmas);
LocalDateTime Local DateTime
LocalDateTimeは時間と日付の両方を表します。これは、最初の2つのセクションの内容のマージと1つのオブジェクトに相当します。 LocalDateTimeは、LocalTimeやLocalDateと同様に、どちらも不変です。 LocalDateTimeは、特定のフィールドにアクセスするためのいくつかの方法を提供します。
コードコピーは次のとおりです。
LocalDateTime sylvester = localdateTime.of(2014年、月、12月、31、23、59、59);
Dayofweek dayofweek = sylvester.getDayofweek();
System.out.println(dayofweek);
月月= sylvester.getmonth();
System.out.println(月);
long minuteofday = sylvester.getlong(chronofield.minute_of_day);
System.out.println(minuteofday);
タイムゾーン情報を取り付けるだけで、時間のインスタントオブジェクトに変換できます。
コードコピーは次のとおりです。
インスタントインスタント=シルベスター
.atzone(Zoneid.systemDefault())
.toinstant();
date legacydate = date.from(instant);
System.out.println(LegacyDate);
LocalDateTimeのフォーマットは、定義された形式を使用することに加えて、次の形式を定義することと同じです。
コードコピーは次のとおりです。
DateTimeFormatter Formatter =
DateTimeFormatter
.ofpattern( "mmm dd、yyyy -hh:mm");
localdateTime parsed = localdateTime.parse( "2014年11月3日-07:13"、フォーマッタ);
string string = formatter.format(parsed);
System.out.println(文字列); // 2014年11月 - 07:13
Java.text.numberformatとは異なり、DateTimeFormatterの新しいバージョンは不可能なので、スレッドセーフです。
日付と日付の詳細:http://download.java.net/jdk8/docs/api/java/time/format/datetimeformatter.html
10。注釈ノート
Java 8では複数の注釈がサポートされています。まず、その意味を理解するために例を見てみましょう。
まず、パッケージ化されたヒント注釈を定義して、特定のヒント注釈を配置します。
コードコピーは次のとおりです。
@interfaceヒント{
hint [] value();
}
@repeatable(hints.class)
@interfaceヒント{
string値();
}
Java 8を使用すると、同じタイプの注釈を複数回使用でき、Annotation @Repeatableにラベルを付けるだけです。
例1:ラッパークラスをコンテナとして使用して、複数の注釈を保存します(古い方法)
コードコピーは次のとおりです。
@hints({ @hint( "hint1")、@hint( "hint2")})
クラスの人{}
例2:複数の注釈の使用(新しい方法)
コードコピーは次のとおりです。
@hint( "hint1")
@hint( "hint2")
クラスの人{}
2番目の例では、Javaコンパイラは@hintsアノテーションを暗黙的に定義します。
コードコピーは次のとおりです。
ヒントhint = person.class.getAnnotation(hint.class);
System.out.println(ヒント);
ヒントhints1 = person.class.getannotation(hints.class);
System.out.println(hints1.value()。長さ);
hint [] hints2 = person.class.getAnnotationsbyType(hint.class);
System.out.println(hints2.length);
@hints Annotation on the Person Classを定義していなくても、getannotation(hints.class)を通じて@hintsアノテーションを取得できます。
さらに、Java 8アノテーションが2つの新しいターゲットに追加されました。
コードコピーは次のとおりです。
@target({elementType.type_parameter、elementType.type_use})
@interface myannotation {}
Java 8の新機能に関するものであり、発見されるのを待っている機能が間違いなくあります。 JDK 1.8には、Arrays.Parallelsort、StampedLock、CompleteableFutureなど、多くの有用なものがあります。