JavaRush /Java Blog /Random-JA /Java 8 の機能 – 究極のガイド (パート 1)
0xFF
レベル 9
Донецк

Java 8 の機能 – 究極のガイド (パート 1)

Random-JA グループに公開済み
記事Java 8 features – The ULTIMATE Guideの翻訳の最初の部分。後編はこちら(リンクは変更になる場合があります)。 Java 8 の機能 – 究極のガイド (パート 1) - 1 編集者注: この記事は Java 8 が一般公開されていたときに公開されたものであり、これが確かにメジャー バージョンであることを示しています。ここでは、 「Playing with Java 8 – Lambdas and Concurrency」、「Java 8 Date and Time API Guide: LocalDateTime」 、「Abstract Class vs. Interface in the Java 8 Era 」など、Java Code Geeks ガイドを豊富に提供しています。また、他のソースからの 15 の必読 Java 8 チュートリアルへのリンクもあります。もちろん、Java 8 のダークサイドなどのいくつかの欠点にも注目します。そこで、利便性を高めるために、Java 8 のすべての主要な機能を 1 か所に集めてみましょう。楽しむ!

1. はじめに

疑いもなく、Java 8 のリリースは、Java 5 (かなり昔、2004 年にリリースされました) 以来最大のイベントです。これは、言語、コンパイラ、ライブラリ、ツール、および JVM (Java 仮想マシン) の両方において Java に多くの新機能をもたらしました。このチュートリアルでは、これらの変更を確認し、実際の例を使用してさまざまな使用例を示します。このガイドはいくつかの部分で構成されており、各部分ではプラットフォームの特定の側面について説明します。
  • 言語
  • コンパイラ
  • 図書館
  • ツール
  • ランタイム環境 (JVM)

2. Java 8 の新機能

いずれにしても、Java 8 はメジャー リリースです。これほどの時間がかかったのは、すべての Java 開発者が求めていた機能の実装があったからだと言えます。このセクションでは、それらのほとんどについて説明します。

2.1. ラムダと関数型インターフェイス

ラムダ (プライベート メソッドまたは匿名メソッドとも呼ばれる) は、Java 8 リリース全体で最も大きく、最も予想される言語変更であり、機能をメソッドの引数として指定したり (関数を宣言することで)、コードをデータとして指定したりすることができます。 : すべての関数開発者がよく知っている概念。JVM プラットフォーム上の多くの言語 (Groovy、Scalaなど) には当初からラムダがありましたが、Java 開発者には匿名クラスを通じてラムダを表現する以外に選択肢はありませんでした。ラムダ設計の議論には多くの時間と労力が公にされました。しかし最終的には妥協点が見つかり、新しい簡潔なデザインが誕生しました。最も単純な形式では、ラムダはパラメータ、 –>シンボル、および本体のカンマ区切りリストとして表すことができます。例えば:
Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) )
引数eの型はコンパイラによって決定されることに注意してください。さらに、パラメータを括弧で囲むことにより、パラメータのタイプを明示的に指定できます。例えば:
Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) );
ラムダ本体がより複雑な場合は、Java の通常の関数定義と同様に、中括弧で囲むことができます。例えば:
Arrays.asList( "a", "b", "d" ).forEach( e -< {
    System.out.print( e );
    System.out.print( e );
} );
finalラムダはクラス メンバーとローカル変数を参照できます (フィールドがアクセスされている かどうかに関係なく、暗黙的に呼び出しが有効になります)。たとえば、次の 2 つのスニペットは同等です。
String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach(
    ( String e ) -> System.out.print( e + separator ) );
そして:
final String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach(
    ( String e ) -> System.out.print( e + separator ) );
ラムダは値を返すことができます。戻り値の型はコンパイラによって決定されます。returnラムダの本体が 1 行で構成される場合、宣言は必要ありません。以下の 2 つのコード スニペットは同等です。
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
そして:
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {
    int result = e1.compareTo( e2 );
    return result;
} );
言語開発者は、既存の関数をラムダ対応にする方法について長い間考えてきました。その結果、機能インターフェイスの概念が登場しました。関数型インターフェイスは、メソッドが 1 つだけあるインターフェイスです。その結果、暗黙的にラムダ式に変換できます。java.lang.Runnableそして、java.util.concurrent.Callable機能インターフェイスの 2 つの優れた例です。実際には、関数型インターフェイスは非常に脆弱です。誰かがインターフェイス定義に他のメソッドを 1 つでも追加すると、そのメソッドは機能しなくなり、コンパイル プロセスは完了しません。この脆弱性を回避し、インターフェイスの意図を機能的であると明示的に定義するために、Java 8 では特別なアノテーションが追加されました@FunctionalInterface(Java ライブラリ内のすべての既存のインターフェイスは @FunctionalInterface アノテーションを受け取りました)。関数インターフェースの単純な定義を見てみましょう。
@FunctionalInterface
public interface Functional {
    void method();
}
留意すべき点が 1 つあります。デフォルト メソッドと静的メソッドは関数型インターフェイスの原則に違反せず、次のように宣言できます。
@FunctionalInterface
public interface FunctionalDefaultMethods {
    void method();

    default void defaultMethod() {
    }
}
ラムダは Java 8 の最も人気のある機能です。ラムダは、より多くの開発者をこの素晴らしいプラットフォームに引きつけ、純粋な Java の機能に対するスマートなサポートを提供する可能性を秘めています。詳細については、公式ドキュメントを参照してください。

2.2. デフォルトのインターフェースと静的メソッド

Java 8 では、デフォルト メソッドと静的メソッドという 2 つの新しい概念を使用してインターフェイスの定義が拡張されました。デフォルトのメソッドはインターフェイスをトレイトに似たものにしますが、目的は少し異なります。これらのインターフェイスを使用すると、以前に作成されたバージョンのインターフェイスとの下位互換性を損なうことなく、既存のインターフェイスに新しいメソッドを追加できます。デフォルト メソッドと抽象メソッドの違いは、抽象メソッドは実装する必要があるが、デフォルト メソッドは実装しないことです。代わりに、各インターフェイスはいわゆるデフォルト実装を提供する必要があり、すべての子孫はデフォルトでそれを受け取ります (必要に応じてこのデフォルト実装をオーバーライドできます)。以下の例を見てみましょう。
private interface Defaulable {
    // Интерфейсы теперь разрешают методы по умолчанию,
    // клиент может реализовывать  (переопределять)
    // or не реализовывать его
    default String notRequired() {
        return "Default implementation";
    }
}

private static class DefaultableImpl implements Defaulable {
}

private static class OverridableImpl implements Defaulable {
    @Override
    public String notRequired() {
        return "Overridden implementation";
    }
}
インターフェイスは、メソッド定義の一部としてキーワードを使用してDefaulableデフォルトのメソッドを宣言します。クラスの 1 つである は、デフォルトのメソッドをそのままにしてこのインターフェイスを実装します。別のクラス はデフォルトの実装をオーバーライドし、独自の実装を提供します。Java 8 で導入されたもう 1 つの興味深い機能は、インターフェイスで静的メソッドを宣言 (およびその実装を提供) できることです。以下に例を示します。 notRequired()defaultDefaultableImplOverridableImpl
private interface DefaulableFactory {
    // Interfaces now allow static methods
    static Defaulable create( Supplier<Defaulable> supplier ) {
        return supplier.get();
    }
}
小さなコードでは、上記の例のデフォルト メソッドと静的メソッドを組み合わせています。
public static void main( String[] args ) {
    Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new );
    System.out.println( defaulable.notRequired() );

    defaulable = DefaulableFactory.create( OverridableImpl::new );
    System.out.println( defaulable.notRequired() );
}
このプログラムのコンソール出力は次のようになります。
Default implementation
Overridden implementation
JVM のデフォルトのメソッド実装は非常に効率的で、メソッド呼び出しはバイトコード命令によってサポートされています。デフォルトのメソッドを使用すると、コンパイル プロセスを中断することなく、既存の Java インターフェイスを進化させることができました。良い例としては、インターフェイスに追加された多くのメソッドがありますjava.util.Collection: stream()parallelStream()forEach()removeIf()、 ... 強力ですが、デフォルト メソッドは注意して使用する必要があります。デフォルト メソッドを宣言する前に、これが本当に必要かどうかをよく考えてください。複雑な階層におけるコンパイルの曖昧さとエラーに対処します。詳細については、ドキュメントを参照してください。

2.3. 参考方法

参照メソッドは、Java クラスまたはオブジェクト (インスタンス) の既存のメソッドまたはコンストラクターを参照するための便利な構文を実装します。参照メソッドをラムダ式と組み合わせると、言語構造がコンパクトかつ簡潔になり、テンプレートベースになります。以下はさまざまなメソッド定義の例としてのクラスですCar。サポートされている 4 つのタイプの参照メソッドを強調してみましょう。
public static class Car {
    public static Car create( final Supplier<Car> supplier ) {
        return supplier.get();
    }

    public static void collide( final Car car ) {
        System.out.println( "Collided " + car.toString() );
    }

    public void follow( final Car another ) {
        System.out.println( "Following the " + another.toString() );
    }

    public void repair() {
        System.out.println( "Repaired " + this.toString() );
    }
}
最初の参照メソッドは、構文を使用したコンストラクターへの参照Class::new、またはジェネリックスの代替ですClass< T >::new。コンストラクターには引数がないことに注意してください。
final Car car = Car.create( Car::new );
final List<Car> cars = Arrays.asList( car );
2 番目のオプションは、構文を使用した静的メソッドへの参照ですClass::static_method。このメソッドは、 type のパラメータを 1 つだけ取ることに注意してくださいCar
cars.forEach( Car::collide );
3 番目のタイプは、構文を使用した特定のタイプの任意のオブジェクトのインスタンスのメソッドへの参照ですClass::method。このメソッドでは引数が受け入れられないことに注意してください。
cars.forEach( Car::repair );
最後の 4 番目のタイプは、構文 を使用した特定のクラスのインスタンスのメソッドへの参照ですinstance::method。このメソッドはタイプのパラメータを 1 つだけ受け入れることに注意してくださいCar
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
これらの例をすべて Java プログラムとして実行すると、次のコンソール出力が生成されます (クラス参照はCar異なる場合があります)。
Collided com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Repaired com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Following the com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
より詳細な情報や参照方法の詳細については、公式ドキュメントを参照してください。

2.4. 重複した注釈

Java 5 でアノテーションのサポートが導入されて以来、この機能は非常に人気があり、非常に広く使用されるようになりました。ただし、アノテーションを使用する場合の制限の 1 つは、同じアノテーションを同じ場所で複数回宣言できないという事実でした。Java 8 ではこのルールが破られ、重複したアノテーションが導入されます。これにより、同じアノテーションが宣言された場所で複数回繰り返すことができます。重複するアノテーションは、 annotation を使用してそれ自体にアノテーションを付ける必要があります@Repeatable。実際、これは言語の変更というよりも、コンパイラのトリックによるものですが、手法は同じままです。簡単な例を見てみましょう。
package com.javacodegeeks.java8.repeatable.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class RepeatingAnnotations {
    @Target( ElementType.TYPE )
    @Retention( RetentionPolicy.RUNTIME )
    public @interface Filters {
        Filter[] value();
    }

    @Target( ElementType.TYPE )
    @Retention( RetentionPolicy.RUNTIME )
    @Repeatable( Filters.class )
    public @interface Filter {
        String value();
    };

    @Filter( "filter1" )
    @Filter( "filter2" )
    public interface Filterable {
    }

    public static void main(String[] args) {
        for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
            System.out.println( filter.value() );
        }
    }
}
見てわかるように、クラスFilterには @Repeatable( Filters. class) の注釈が付けられています。Filtersは単なる annotations の所有者ですFilterが、Java コンパイラーはその存在を開発者から隠そうとします。したがって、インターフェースには( を記述せずに) 2 回宣言されたFilterableアノテーションが含まれています。Reflection API は、あるタイプの重複したアノテーションを返すための新しいメソッドも提供します (Filterable. .getAnnotation( Filters. ) はコンパイラによって挿入されたインスタンスを返すことに注意してください)。プログラムの出力は次のようになります。 FilterFiltersgetAnnotationsByType()classclassFilters
filter1
filter2
詳細については、公式ドキュメントを参照してください。

2.5. 型推論の改善

Java 8 コンパイラーには、多くの型推論の改善が加えられています。多くの場合、明示的な型パラメータをコンパイラで定義できるため、コードがすっきりします。一例を見てみましょう。
package com.javacodegeeks.java8.type.inference;

public class Value<T> {
    public static<T> T defaultValue() {
        return null;
    }

    public T getOrDefault( T value, T defaultValue ) {
        return ( value != null ) ? value : defaultValue;
    }
}
そして、これが type での使用ですValue<String>:
package com.javacodegeeks.java8.type.inference;

public class TypeInference {
    public static void main(String[] args) {
        final Value<String> value = new Value<>();
        value.getOrDefault( "22", Value.defaultValue() );
    }
}
type パラメータValue.defaultValue()は自動的に決定されるため、明示的に指定する必要はありません。Java 7 では、同じ例はコンパイルされないため、<NOBR>Value.<String>defaultValue()</NOBR> として書き換える必要があります。

2.6. 拡張された注釈サポート

Java 8 では、アノテーションを使用できるコンテキストが拡張されています。現在では、ローカル変数、ジェネリック型、スーパークラス、実装されたインターフェイス、さらにはメソッド例外など、ほとんどすべてのものにアノテーションを付けることができます。いくつかの例を以下に示します。
package com.javacodegeeks.java8.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collection;

public class Annotations {
    @Retention( RetentionPolicy.RUNTIME )
    @Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )
    public @interface NonEmpty {
    }

    public static class Holder<@NonEmpty T> extends @NonEmpty Object {
        public void method() throws @NonEmpty Exception {
        }
    }

    @SuppressWarnings( "unused" )
    public static void main(String[] args) {
        final Holder<String> holder = new @NonEmpty Holder<String>();
        @NonEmpty Collection<@NonEmpty String> strings = new ArrayList<>();
    }
}
ElementType.TYPE_USEElementType.TYPE_PARAMETER関連する注釈コンテキストを記述するための 2 つの新しい要素タイプ。Annotation Processing APIまた、Java の新しい注釈タイプを認識するために小さな変更が加えられました。

3. Java コンパイラの新機能

3.1. パラメータ名

長い間、Java 開発者は、メソッド パラメータ名を Java バイトコードに格納して実行時に使用できるようにするさまざまな方法を発明してきました (たとえば、Paranamer ライブラリ)。最後に、Java 8 は、この難しい関数を言語 (Reflection API とメソッドを使用Parameter.getName()) とバイトコード (新しいコンパイラ引数javac:)で作成します–parameters
package com.javacodegeeks.java8.parameter.names;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class ParameterNames {
    public static void main(String[] args) throws Exception {
        Method method = ParameterNames.class.getMethod( "main", String[].class );
        for( final Parameter parameter: method.getParameters() ) {
            System.out.println( "Parameter: " + parameter.getName() );
        }
    }
}
引数を使用せずにこのクラスをコンパイルし–parametersてプログラムを実行すると、次のような結果が表示されます。
Parameter: arg0
パラメーターが–parametersコンパイラーに渡されると、プログラム出力は異なります (パラメーターの実際の名前が表示されます)。
Parameter: args
上級 Maven ユーザー の場合は、セクションを使用して –parameters 引数をコンパイルに追加できますmaven-compiler-plugin
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.1</version>
    <configuration>
    <compilerArgument>-parameters</compilerArgument>
    <source>1.8</source>
    <target>1.8</target>
    </configuration>
</plugin>
isNamePresent()パラメータ名が利用可能かどうかを確認するには、クラスが提供する 便利なメソッドがありますParameter

4. 新しい Java ツール

Java 8 には、新しいコマンド ライン ツールのセットが付属しています。このセクションでは、それらの中で最も興味深いものを見ていきます。

4.1. ナスホルンエンジン: jjs

jjs は、コマンド ライン ベースのスタンドアロン Nashorn エンジンです。JavaScript ソース コード ファイルのリストを取得して実行します。たとえば、次の内容を含む func.jsファイルを作成してみましょう。
function f() {
     return 1;
};

print( f() + 1 );
このファイルを実行するには、それを引数としてjjsに渡します。
jjs func.js
コンソール出力は次のようになります。
2
詳細については、ドキュメントを参照してください

4.2. クラス依存関係アナライザー: jdeps

jdeps は非常に優れたコマンドライン ツールです。Java クラスのパッケージまたはクラス レベルの依存関係を示します。.classファイル、フォルダー、またはJAR ファイルを入力として受け入れます。デフォルトでは、jdeps は依存関係を標準出力 (コンソール) に出力します。例として、人気のあるSpring Frameworkライブラリの依存関係レポートを見てみましょう。例を短くするために、JAR ファイルのみの依存関係を見てみましょうorg.springframework.core-3.0.5.RELEASE.jar
jdeps org.springframework.core-3.0.5.RELEASE.jar
このコマンドは非常に多くの出力を出力するため、出力の一部のみを分析します。依存関係はパッケージごとにグループ化されます。依存関係がない場合は、「見つかりません」と表示されます。
org.springframework.core-3.0.5.RELEASE.jar -> C:\Program Files\Java\jdk1.8.0\jre\lib\rt.jar
   org.springframework.core (org.springframework.core-3.0.5.RELEASE.jar)
      -> java.io
      -> java.lang
      -> java.lang.annotation
      -> java.lang.ref
      -> java.lang.reflect
      -> java.util
      -> java.util.concurrent
      -> org.apache.commons.logging                         not found
      -> org.springframework.asm                            not found
      -> org.springframework.asm.commons                    not found
   org.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar)
      -> java.lang
      -> java.lang.annotation
      -> java.lang.reflect
      -> java.util
詳細については、公式ドキュメントを参照してください。
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION