JavaRush /Java Blog /Random-KO /Java 8 기능 – 최종 가이드(1부)
0xFF
레벨 9
Донецк

Java 8 기능 – 최종 가이드(1부)

Random-KO 그룹에 게시되었습니다
Java 8 기능 - ULTIMATE 가이드 기사 번역의 첫 번째 부분입니다 . 두 번째 부분은 여기에 있습니다 (링크는 변경될 수 있음). Java 8 기능 – 최종 가이드(1부) - 1 편집자 주 : 이 기사는 Java 8이 대중에게 공개되었을 때 게시되었으며 모든 표시에 따르면 이것이 실제로 주요 버전입니다. 여기에서는 Java 8 사용 – 람다 및 동시성 , Java 8 날짜 및 시간 API 가이드: LocalDateTimeJava 8 시대의 추상 클래스 대 인터페이스와 같은 풍부한 Java Code Geeks 가이드를 제공했습니다 . 또한 다른 소스에서 꼭 읽어야 할 Java 8 튜토리얼 15개 에 대한 링크도 제공합니다 . 물론 우리는 Java 8의 어두운 면 과 같은 몇 가지 단점을 살펴봅니다 . 이제 사용자의 편의를 위해 Java 8의 모든 주요 기능을 한곳에 모을 차례입니다. 즐기다!

1. 소개

의심할 여지 없이 Java 8의 출시는 Java 5(꽤 오래 전인 2004년에 출시됨) 이후 가장 큰 이벤트입니다. 이는 언어, 컴파일러, 라이브러리, 도구 및 JVM(Java Virtual Machine) 모두에서 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람다 본문이 한 줄로 구성된 경우 선언이 필요하지 않습니다. 아래 두 코드 조각은 동일합니다.
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;
} );
언어 개발자들은 기존 함수를 람다 친화적으로 만드는 방법에 대해 오랫동안 고민했습니다. 결과적으로 기능적 인터페이스(Functional Interface)라는 개념이 등장했습니다. 기능적 인터페이스는 메서드가 하나만 있는 인터페이스입니다. 결과적으로 암시적으로 람다 식으로 변환될 수 있습니다. java.lang.Runnable그리고 java.util.concurrent.Callable기능적 인터페이스의 두 가지 훌륭한 예입니다. 실제로 기능적 인터페이스는 매우 취약합니다. 누군가가 인터페이스 정의에 다른 메서드를 하나라도 추가하면 해당 인터페이스는 더 이상 기능하지 않고 컴파일 프로세스가 완료되지 않습니다. 이러한 취약성을 방지하고 인터페이스의 의도를 기능적으로 명시적으로 정의하기 위해 Java 8에 특수 주석이 추가되었습니다 @FunctionalInterface(Java 라이브러리의 모든 기존 인터페이스는 @FunctionalInterface 주석을 받았습니다). 기능적 인터페이스에 대한 간단한 정의를 살펴보겠습니다.
@FunctionalInterface
public interface Functional {
    void method();
}
명심해야 할 한 가지가 있습니다: 기본 메소드와 정적 메소드는 기능적 인터페이스의 원칙을 위반하지 않으며 선언될 수 있습니다.
@FunctionalInterface
public interface FunctionalDefaultMethods {
    void method();

    default void defaultMethod() {
    }
}
Lambda는 Java 8의 가장 인기 있는 기능입니다. Lambda는 이 멋진 플랫폼에 더 많은 개발자를 유치하고 순수 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();
    }
}
작은 코드 조각은 위 예제의 기본 메서드와 정적 메서드를 결합합니다.
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. 지원되는 네 가지 참조 메서드 유형을 강조해 보겠습니다.
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 );
세 번째 유형은 구문을 사용하여 특정 유형의 임의 객체 인스턴스의 메서드에 대한 참조입니다 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
자세한 내용 및 참조 방법에 대한 자세한 내용은 공식 문서를 참조하세요 .

2.4. 중복된 주석

Java 5에서 주석 지원을 도입한 이후로 이 기능은 매우 널리 사용되고 널리 사용됩니다. 그러나 주석 사용의 한계 중 하나는 동일한 주석이 동일한 위치에 두 번 이상 선언될 수 없다는 사실이었습니다. Java 8은 이 규칙을 깨고 중복 주석을 도입합니다. 이를 통해 동일한 주석이 선언된 위치에서 여러 번 반복될 수 있습니다. 중복 주석은 주석을 사용하여 자체적으로 주석을 달아야 합니다 @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단순히 주석의 소유자이지만 FilterJava 컴파일러는 개발자에게 주석의 존재를 숨기려고 합니다. 따라서 인터페이스에는 두 번 선언된 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;
    }
}
다음은 유형을 사용한 사용법입니다 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>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관련 주석 컨텍스트를 설명하는 두 가지 새로운 요소 유형이 있습니다 . 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 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