JavaRush /Java блогы /Random-KK /Java 8 мүмкіндіктері – түпкілікті нұсқаулық (1 бөлім)
0xFF
Деңгей
Донецк

Java 8 мүмкіндіктері – түпкілікті нұсқаулық (1 бөлім)

Топта жарияланған
Java 8 Features – The ULTIMATE Guide мақаласының аудармасының бірінші бөлімі . Екінші бөлім осында (сілтеме өзгеруі мүмкін). Java 8 мүмкіндіктері – Соңғы нұсқаулық (1-бөлім) - 1 Редактордың ескертпесі : Бұл мақала Java 8 көпшілікке қолжетімді болған кезде жарияланды және барлық белгілер бұл шын мәнінде негізгі нұсқа екенін көрсетеді. Мұнда біз Java 8-мен ойнау – Lambdas және Concurrency , Java 8 Date and Time API нұсқаулығы: LocalDateTime және Java 8 дәуіріндегі дерексіз класс пен интерфейс сияқты көптеген Java Code Geeks нұсқаулығын ұсындық . Біз сондай-ақ басқа көздерден алынған 15 Java 8 оқулықтарына сілтеме жасаймыз . Әрине, біз кейбір кемшіліктерді қарастырамыз, мысалы, Java 8-нің қараңғы жағы . Сонымен, сізге ыңғайлы болу үшін Java 8-нің барлық негізгі мүмкіндіктерін бір жерде жинаудың уақыты келді. Ләззат алыңыз!

1. Кіріспе

Әрине, Java 8 нұсқасының шығарылуы Java 5-тен кейінгі ең үлкен оқиға (баяғыда, 2004 жылы шыққан). Ол Java-ға тілде, компиляторда, кітапханаларда, құралдарда және JVM (Java Virtual Machine) сияқты көптеген жаңа мүмкіндіктер әкелді. Бұл оқулықта біз осы өзгерістерді қарастырамыз және нақты өмірлік мысалдармен әртүрлі пайдалану жағдайларын көрсетеміз. Нұсқаулық бірнеше бөліктерден тұрады, олардың әрқайсысы платформаның белгілі бір аспектісін қарастырады:
  • Тіл
  • Құрастырушы
  • Кітапханалар
  • Құралдар
  • Орындау ортасы (JVM)

2. Java 8-дегі жаңа мүмкіндіктер

Кез келген жағдайда Java 8 негізгі шығарылым болып табылады. Әрбір Java әзірлеушісі іздеген мүмкіндіктерді іске асырудың арқасында бұл өте ұзақ болды деп айта аламыз. Бұл бөлімде біз олардың көпшілігін қарастырамыз.

2.1. Ламбдалар және функционалдық интерфейстер

Ламбдалар (жеке немесе анонимді әдістер ретінде де белгілі) бүкіл Java 8 шығарылымындағы ең үлкен және күтілетін тіл өзгерісі болып табылады.Олар функционалдылықты әдіс аргументі ретінде көрсетуге (оның айналасындағы функцияны жариялау арқылы) немесе codeты деректер ретінде көрсетуге мүмкіндік береді. : әрбір функционалды әзірлеушіге таныс ұғымдар . 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Ламбданың денесі бір жолдан тұратын болса, мәлімдеме талап етілмейді. Төмендегі екі code үзінділері баламалы:
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;
} );
Тіл әзірлеушілері бұрыннан бар функцияларды ламбдаға ыңғайлы ету туралы ұзақ уақыт ойлады. Нәтижесінде функционалдық интерфейс ұғымы пайда болды. Функционалды интерфейс – бұл бір ғана әдісті қамтитын интерфейс. Нәтижесінде оны ламбда өрнегіне жанама түрде түрлендіруге болады. java.lang.Runnableжәне java.util.concurrent.Callableфункционалды интерфейстердің екі тамаша мысалы. Іс жүзінде функционалды интерфейстер өте нәзік: егер біреу интерфейс анықтамасына тіпті бір басқа әдісті қосса, ол енді функционалды болмайды және компиляция процесі аяқталмайды. Бұл нәзіктікті болдырмау және интерфейстің функционалдық мақсатын нақты анықтау үшін Java 8-де арнайы annotation қосылды @FunctionalInterface(Java кітапханасындағы барлық бар интерфейстер @FunctionalInterface annotationсын алды). Функционалдық интерфейстің қарапайым анықтамасын қарастырайық:
@FunctionalInterface
public interface Functional {
    void method();
}
Есте сақтау керек бір нәрсе бар: әдепкі әдістер мен статикалық әдістер функционалды интерфейс принципін бұзбайды және оларды жариялауға болады:
@FunctionalInterface
public interface FunctionalDefaultMethods {
    void method();

    default void defaultMethod() {
    }
}
Ламбдалар - Java 8-нің ең танымал мүмкіндігі. Олардың осы тамаша платформаға көбірек әзірлеушілерді тартуға және таза Java-дағы мүмкіндіктерге ақылды қолдау көрсетуге барлық мүмкіндіктері бар. Толық ақпарат алу үшін ресми құжаттаманы қараңыз .

2.2. Әдепкі интерфейстер және статикалық әдістер

Java 8 интерфейстердің анықтамасын екі жаңа ұғыммен кеңейтті: әдепкі әдіс және статикалық әдіс. Әдепкі әдістер интерфейстерді белгілерге біршама ұқсас етеді, бірақ сәл басқа мақсатқа қызмет етеді. Олар сол интерфейстердің бұрын жазылған нұсқалары үшін кері үйлесімділікті бұзбай бар интерфейстерге жаңа әдістерді қосуға мүмкіндік береді. Әдепкі әдістер мен дерексіз әдістердің айырмашылығы - әдепкі әдістер орындалмаған кезде дерексіз әдістер орындалуы керек. Оның орнына, әрбір интерфейс әдепкі деп аталатын енгізуді қамтамасыз етуі керек және барлық ұрпақтар оны әдепкі бойынша алады (қажет болса, осы әдепкі іске асыруды қайта анықтау мүмкіндігімен). Төмендегі мысалды қарастырайық.
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әдепкі әдісті жариялайды . Сыныптардың бірі, , бұл интерфейсті әдепкі әдісті сол күйінде қалдырып жүзеге асырады. Басқа класс, , әдепкі іске асыруды қайта анықтайды және өзінің өзін қамтамасыз етеді. Java 8-де енгізілген тағы бір қызықты мүмкіндік - интерфейстер статикалық әдістерді жариялай алады (және олардың орындалуын ұсына алады). Міне, мысал: notRequired()defaultDefaultableImplOverridableImpl
private interface DefaulableFactory {
    // Interfaces now allow static methods
    static Defaulable create( Supplier<Defaulable> supplier ) {
        return supplier.get();
    }
}
Кішкентай code бөлігі әдепкі әдіс пен жоғарыдағы мысалдағы статикалық әдісті біріктіреді:
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 жүйесінде әдепкі әдісті іске асыру өте тиімді және әдісті шақыруға byte code нұсқаулары қолдау көрсетеді. Әдепкі әдістер бар Java интерфейстерін компиляция процесін бұзбай дамытуға мүмкіндік берді. Жақсы мысалдар интерфейске қосылған көптеген әдістер java.util.Collection: stream(), parallelStream(), forEach(), removeIf(), ... Күшті болғанымен, әдепкі әдістер сақтықпен пайдаланылуы керек: әдепкі әдісті жарияламас бұрын, бұл шынымен қажет пе туралы екі рет ойлану керек, себебі бұл әкелуі мүмкін. күрделі иерархиялардағы түсініксіздіктер мен қателерді құрастыру. Толық ақпарат алу үшін құжаттаманы қараңыз .

2.3. Анықтамалық әдістер

Анықтамалық әдістер бар әдістерге немесе Java сыныптарының немесе нысандарының (даналары) конструкторларына сілтеме жасау үшін пайдалы синтаксисті жүзеге асырады. Ламбда өрнектерімен бірге сілтеме әдістері тіл конструкцияларын ықшам және қысқа етіп жасайды, бұл оны үлгіге негізделген етеді. Төменде әртүрлі әдіс анықтамаларының мысалы ретінде класс берілген Car, анықтамалық әдістердің қолдау көрсетілетін төрт түрін бөліп көрсетейік:
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 );
Екінші нұсқа - синтаксисі бар статикалық әдіске сілтеме Class::static_method. Әдіс түрдің дәл бір параметрін қабылдайтынын ескеріңіз Car.
cars.forEach( Car::collide );
Үшінші түрі - синтаксисі бар белгілі бір типтегі ерікті an objectінің данасы әдісіне сілтеме Class::method. Әдіспен ешқандай аргумент қабылданbyteынын ескеріңіз.
cars.forEach( Car::repair );
Ал соңғы, төртінші түрі - синтаксисі бар белгілі бір класс данасы әдісіне сілтеме instance::method. Бұл әдіс тек бір типті параметрді қабылдайтынын ескеріңіз 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. Қайталанатын annotationлар

Java 5 annotationларды қолдауды енгізгендіктен , бұл мүмкіндік өте танымал болды және өте кең қолданылады. Дегенмен, annotationларды пайдаланудағы шектеулердің бірі сол annotationны бір жерде бірнеше рет жариялауға болмайтындығы болды. Java 8 бұл ережені бұзады және қайталанатын annotationларды енгізеді. Бұл бірдей annotationларды олар жарияланған жерде бірнеше рет қайталауға мүмкіндік береді. Қайталанатын annotationлар annotationны пайдаланып өздеріне 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() );
        }
    }
}
Көріп отырғанымыздай, сыныпқа @Repeatable( Сүзгілер. ) Filterарқылы түсініктеме берілген . жай ғана annotationлардың иесі , бірақ Java компиляторы олардың қатысуын әзірлеушілерден жасыруға тырысады. Осылайша, интерфейс екі рет жарияланған annotationларды қамтиды (ескертусіз ). Reflection API сонымен қатар кейбір түрдегі қайталанатын annotationларды қайтарудың жаңа әдісін ұсынады (Сүзуге болатын. .getAnnotation( Сүзгілер. ) компилятор енгізген дананы қайтаратынын есте сақтаңыз ). Бағдарламаның нәтижесі келесідей болады: classFiltersFilterFilterableFilterFiltersgetAnnotationsByType()classclassFilters
filter1
filter2
Толық ақпарат алу үшін ресми құжаттаманы қараңыз .

2.5. Жақсартылған түр туралы қорытынды

Java 8 компиляторы көптеген түрдегі қорытынды жақсартуларын алды. Көптеген жағдайларда, анық типті параметрлерді компилятор анықтауы мүмкін, осылайша codeты тазартады. Бір мысалды қарастырайық:
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;
    }
}
Міне, түрімен пайдалану 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() );
    }
}
Түр параметрі Value.defaultValue()автоматты түрде анықталады және оны нақты көрсету қажет емес. Java 7-де бірдей мысал құрастырылмайды және оны <NOBR>Мән.<String>defaultValue()</NOBR> ретінде қайта жазу керек.

2.6. Кеңейтілген annotationларды қолдау

Java 8 annotationларды қолдануға болатын контекстті кеңейтеді. Қазіргі уақытта кез келген дерлік annotation болуы мүмкін: жергілікті айнымалылар, жалпы типтер, суперкласстар және іске асырылған интерфейстер, тіпті әдіс ерекшеліктері. Төменде бірнеше мысал келтірілген:
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_USEжәне ElementType.TYPE_PARAMETERсәйкес annotation мәтінмәнін сипаттайтын екі жаңа элемент түрі. Annotation Processing APIJava тіліндегі жаңа annotation түрлерін тану үшін де аздаған өзгерістерге ұшырады.

3. Java компиляторындағы жаңа мүмкіндіктер

3.1. Параметр атаулары

Уақыт бойы Java әзірлеушілері орындалу уақытында қолжетімді ету үшін Java byte codeында әдіс параметр атауларын сақтаудың әртүрлі жолдарын ойлап тапты (мысалы, Paranamer кітапханасы ). Соңында, Java 8 бұл қиын функцияны тілде (Reflection API және әдісін пайдалану арқылы Parameter.getName()) және byte codeты (жаңа компилятор аргументін пайдалану арқылы 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. Nashorn қозғалтқышы: jjs

jjs - командалық жолға негізделген дербес Nashorn қозғалтқышы. Ол JavaScript бастапқы code файлдарының тізімін алады және оларды іске қосады. Мысалы, келесі мазмұны бар 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