JavaRush /Java блогу /Random-KY /Java 8 Функциялары – Эң мыкты колдонмо (1-бөлүк)
0xFF
Деңгээл
Донецк

Java 8 Функциялары – Эң мыкты колдонмо (1-бөлүк)

Группада жарыяланган
Макаланын котормосунун биринчи бөлүгү Java 8 Features – The ULTIMATE Guide . Экинчи бөлүгү бул жерде (шилтеме өзгөрүшү мүмкүн). Java 8 Функциялары – Акыркы колдонмо (1-бөлүк) - 1 Редакциянын эскертүүсү : Бул макала Java 8 коомчулукка жеткorктүү болуп турганда жарыяланган жана бардык көрсөткүчтөр бул чындап эле негизги version экенин көрсөтүп турат. Бул жерде биз Java 8 менен ойноо – Lambdas жана Concurrency , Java 8 Date and Time API Guide: LocalDateTime жана Java 8 доорундагы абстракттуу класска каршы интерфейс сыяктуу Java Code Geeks гиддерин көп бердик . Биз ошондой эле башка булактардан окуш керек болгон 15 Java 8 окуу куралына шилтеме кылабыз . Албетте, биз кээ бир терс жактарын карап чыгабыз, мисалы, Java 8дин Dark Side . Ошентип, сизге ыңгайлуу болушу үчүн Java 8дин бардык негизги функцияларын бир жерге чогултууга убакыт келди. Enjoy!

1. Киришүү

Албетте, Java 8дин чыгарылышы Java 5тен кийинки эң чоң окуя (бир топ убакыт мурун, 2004-жылы чыгарылган). Ал Java тorне тилде, компилятордо, китепканаларда, куралдарда жана JVMде (Java Virtual Machine) көптөгөн жаңы функцияларды алып келди. Бул окуу куралында биз бул өзгөрүүлөрдү карап чыгабыз жана турмуштук мисалдар менен ар кандай колдонуу учурларын көрсөтөбүз. Колдонмо бир нече бөлүктөн турат, алардын ар бири платформанын белгилүү бир аспектисин карайт:
  • Тил
  • Компилятор
  • Китепканалар
  • Куралдар
  • Runtime Environment (JVM)

2. Java 8деги жаңы функциялар

Кандай болбосун, Java 8 негизги чыгаруу болуп саналат. Ар бир Java иштеп чыгуучусу издеген функциялардын ишке ашуусунан улам көпкө созулду деп айта алабыз. Бул бөлүмдө биз алардын көбүнө токтолобуз.

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

Lambdas (жеке же анонимдүү ыкмалар катары да белгилүү) 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Ламбданын денеси бир саптан турса, декларация талап кылынbyte. Төмөнкү эки 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();
}
Бир нерсени эстен чыгарбоо керек: демейки ыкмалар жана статикалык методдор функционалдык интерфейстин принцибин бузbyte жана аларды жарыялоого болот:
@FunctionalInterface
public interface FunctionalDefaultMethods {
    void method();

    default void defaultMethod() {
    }
}
Lambdas Java 8дин эң популярдуу өзгөчөлүгү. Алар бул сонун платформага көбүрөөк иштеп чыгуучуларды тартууга жана таза Java'дагы функцияларды акылдуу колдоо көрсөтүүгө бардык мүмкүнчүлүктөрү бар. Көбүрөөк маалымат алуу үчүн расмий documentтерди караңыз .

2.2. Демейки интерфейстер жана статикалык методдор

Java 8 интерфейстердин аныктамасын эки жаңы түшүнүк менен кеңейтти: демейки ыкма жана статикалык метод. Демейки ыкмалар интерфейстерди өзгөчөлүктөргө бир аз окшош кылат, бирок бир аз башкача максатты көздөйт. Алар ошол интерфейстердин мурда жазылган versionлары үчүн артка шайкештикти бузбастан, учурдагы интерфейстерге жаңы ыкмаларды кошууга мүмкүндүк берет. Демейки методдор менен абстракттуу методдордун айырмасы абстракттуу методдор ишке ашырылышы керек, ал эми демейки ыкмалар ишке ашпайт. Анын ордуна, ар бир интерфейс демейки ишке ашырууну камсыз кылышы керек жана бардык тукумдар аны демейки боюнча алышат (зарыл болсо, бул демейки ишке ашырууну жокко чыгаруу мүмкүнчүлүгү менен). Келгиле, төмөндөгү мисалды карап көрөлү.
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();
    }
}
Коддун кичинекей бөлүгү демейки ыкманы жана жогорудагы мисалдагы статикалык ыкманы айкалыштырат:
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(), ... Күчтүү болгонуна карабастан, демейки ыкмаларды этияттык менен колдонуу керек: демейки ыкманы жарыялоодон мурун, бул чындап эле керекпи же жокпу, эки жолу ойлонушуңуз керек, анткени бул алып келиши мүмкүн. татаал иерархиялардагы бүдөмүк жана каталарды түзүүгө. Көбүрөөк маалымат алуу үчүн, documentтерди карагыла .

2.3. Шилтеме методдору

Шилтеме методдору Java класстарынын же an objectтеринин (инстанцияларынын) болгон методдоруна же конструкторлоруна шилтеме кылуу үчүн пайдалуу синтаксисти ишке ашырат. Ламбда туюнтмалары менен бирге маалымдама ыкмалары тил конструкцияларын компакттуу жана кыска кылып, аны шаблонго негизделген кылат. Төмөндө ар кандай метод аныктамаларынын үлгүсү катары класс берилген 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. Метод тарабынан эч кандай аргумент кабыл алынбагандыгын эске алыңыз.
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
Көбүрөөк маалымат жана маалымдама ыкмаларынын чоо-жайы үчүн расмий documentтерди караңыз .

2.4. Кайталанма annotationлар

Java 5 annotationларды колдоону киргизгенден бери , бул функция абдан популярдуу болуп, абдан кеңири колдонулат. Бирок annotationларды колдонуудагы чектөөлөрдүн бири бир эле annotationны бир эле жерде бир нече жолу жарыялоого мүмкүн эместиги болгон. Java 8 бул эрежени бузуп, кайталанма 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ланган . жөн гана annotationлардын ээси , бирок Java компилятору иштеп чыгуучулардан алардын катышуусун жашырууга аракет кылат. Ошентип, интерфейс эки жолу жарыяланган annotationларды камтыйт (сөзсүз ). Reflection API ошондой эле кандайдыр бир түрдөгү кайталанма annotationларды кайтаруунун жаңы ыкмасын камсыз кылат (Фыпкалоочу. .getAnnotation( Чыпкалар. ) компилятор тарабынан киргизилген инстанцияны кайтарарын унутпаңыз ). Программанын натыйжасы төмөнкүдөй болот: classFiltersFilterFilterableFilterFiltersgetAnnotationsByType()classclassFilters
filter1
filter2
Көбүрөөк маалымат алуу үчүн расмий documentтерди караңыз .

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де ошол эле мисал компиляцияланbyte жана <NOBR>Value.<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 APIошондой эле Javaдагы жаңы 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
Көбүрөөк маалымат алуу үчүн documentтерди караңыз .

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
Көбүрөөк маалымат алуу үчүн расмий documentтерди караңыз .
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION