16. [Java] Annotation, 람다식, 스트림 / 20230503
🧑🏻💻 TIL(Today I Learned)
🧑🏻💻 Annotation, 람다식, 스트림
1. 애너테이션(Annotation)
➡️ 소스 코드가 컴파일되거나 실행될 때 컴파일러 및 다른 프로그램에게 필요한 정보를 전달 해주는 문법
➡️ 주석은 사람, 즉 사용자에게 정보를 전달한다면 애너테이션은 컴파일러나 다른 프로그램에게 정보 전달
- @ 로 시작하며 클래스, 인터페이스, 필드, 메서드 등에 붙여서 사용가능
- 위 사진의 @Override는 example()이 추상 메서드를 구현하거나 상위 클래스의 메서드를 오버라이딩 한 메서드라는 것을 컴파일에게 알려주는 역할
🔎 애너테이션의 종류
➡️ 기본적으로 JDK에서 제공하는 애너테이션도 있지만 다른 프로그램에서 제공하는 것도 있음
➡️ 구분
- 표준 애너테이션
: JDK에 내장된 일반적인 애너테이션 → 위 예시와 같이 다른 문법 요소에 붙여서 사용하는 것이 일반적 - 메타 애너테이션
: 다른 애너테이션을 정의할 때 사용하는 애너테이션
→ 애너테이션을 직접 정의해서 사용할 때 사용하는 애너테이션
→ 애너테이션을 사용자가 직접 정의해서 사용가능 (사용자 정의 애너테이션)
✍🏻 표준 애너테이션
- @Override
➡️ 메서드 앞에서만 붙일 수 있는 애너테이션
➡️ 선언한 메서드가 상위 클래스의 메서드를 오버라이딩하거나 추상 메서드를 구현하는 메서드라는 것을 컴파일러에게 알려주는 역할
class SuperClass {
public void example() { // 같은 이름을 가진 메서드가 상위 클래스에 있는지 확인 (2)
System.out.println("example() of SuperClass");
}
}
class SubClass extends SuperClass {
@Override // 컴파일 과정에서 발견 (1)
public void example() {
System.out.println("example() of SubClass");
}
}
📍 상위 클래스에 같은 이름의 메서드가 존재하는지 확인하는 이유?
: 어떤 메서드를 오버라이딩하거나 구현할 때 오타가 날 경우 @Override 를 붙여주지 않으면 컴파일러는 그냥 다른 이름의 새로운 메서드를 정의한다고 간주하고 에러를 발생시키지 않음
→ 결국 에러가 났을 때 어디서 에러가 났는지 원인을 찾기 어려움
- @Deprecated(사용이 중단된, 더이상 권장하지 않는)
➡️ 기존에 사용하던 기술이 다른 기술로 대체되어 기존 기술을 적용한 코드를 더 이상 사용하지 않도록 유도하는 경우 사용
➡️ 클래스를 만들어 @Deprecated 붙여준 뒤 인스턴스화하고 getOldField() 호출하면 취소선이 그어지면서 나타남과 함께 is deprected 라고 경고 메세지 출력
➡️ 직접 컴파일하고 확인해 보면 위와 같은 경고 메세지가 나타남
"Deprected" 로 표시된 메서드나 클래스 사용하거나 오버라이드하는 코드를 발견했다는 뜻
➡️ 기존의 코드를 다른 코드와의 호환성 문제로 삭제하기 곤란해 남겨두어야 하지만 더 이상 사용하는 것을 권장하지 않을 때 사용
- @SupperessWarnings
➡️ 컴파일 경고 메세지가 나타나지 않도록 함
➡️ 경고가 발생할 것을 예상하고 있으면서 묵인해야할 때 사용
@SuppressWarnings("all") -- 모든 경고 억제
@SuppressWarnings("deprecation")
@SuppressWarnings({"deprecation", "unused", "null"})
// 위와 같이 괄호를 붙이고 그 안에 억제하고자 하는 경고 메시지 지정
// 하나씩 넣어도 되고 여러 개 넣어줄 수 있음
// 특정 내용과 관련된 경고 메시지 선택적으로 억제가능
@FuntionallInterface
➡️ 함수형 인터페이스를 선언할 때 컴파일러가 함수형 인터페이스의 선언이 바르게 되었는지 확인하도록 함, 바르게 선언되지 않으면 에러!
@FunctionalInterface
public interface ExampleInterface {
public abstract void example(); // 단 하나의 추상 메서드
}
// 함수형 인터페이스는 단 하나의 추상 메서드를 가져야 함!
// 위와 같이 잘 작성되었는지 컴파일러에게 검사할 것을 요구함
🔎 메타 애너테이션
➡️ 애너테이션을 정의하는 데에 사용되는 애너테이션
➡️ 애너테이션의 적용 대상 및 유지 기간을 지정하는 데 사용
- 애너테이션을 정의할 때는 @interface 키워드 사용
- @Target, @Retention 은 @Override의 적용 대상과 유지 기간을 지정하는 역할
@Target
➡️ 애너테이션을 적용할 대상을 지정하는 데 사용
➡️ 아래 표가 나와 있는 내용이 애너테이션을 사용하여 지정할 수 있는 대상의 타입
import static java.lang.annotation.ElementType.*;
//import문을 이용하여 ElementType.TYPE 대신 TYPE과 같이 간단히 작성할 수 있음
@Target({FIELD, TYPE, TYPE_USE}) // 적용대상이 FIELD, TYPE
public @interface CustomAnnotation { } // CustomAnnotation을 정의
@CustomAnnotation // 적용대상이 TYPE인 경우
class Main {
@CustomAnnotation // 적용대상이 FIELD인 경우
int i;
}
@Inherited
➡️ 하위 클래스가 애너테이션을 상속받도록 함
➡️ 상위 클래스에 붙이면 하위 클래스도 상위 클래스에 붙은 애너테이션이 동일하게 적용
@Inherited // @SuperAnnotation이 하위 클래스까지 적용
@interface SuperAnnotation{ }
@SuperAnnotation
class Super { }
class Sub extends Super{ } // Sub에 애너테이션이 붙은 것으로 인식
@Retention
➡️ 특정 애너테이션의 지속 시간 결정하는 데 사용
➡️ 유지 정책(Retention Policy) : 애너테이션이 유지되는 기간을 정하는 속성
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
//오버라이딩이 제대로 되었는지 컴파일러가 확인하는 용도
//클래스 파일에 남길 필요 없이 컴파일 시에만 확인하고 사라짐 --> 실행 시에는 더이상 사용되지 않음
public @interface Override(){ }
@Repeatable
➡️ 애너테이션을 여러 번 붙일 수 있도록 허용한다는 의미
@Repeatable(Works.class) // Work 애너테이션을 여러 번 반복해서 쓸 수 있게 한다.
@interface Work{ // 사용자 타입의 애너테이션 정의
String value();
}
@Work("코드 업데이트")
@Work("메서드 오버라이딩")
class Main{
... 생략 ...
}
@interface Works { // 여러 개의 Work애너테이션을 담을 컨테이너 애너테이션 Works
Work[] value();
}
@Repeatable(Works.class) // 컨테이너 애너테이션 지정
@interface Work {
String value();
}
// 일반적인 애너테이션과는 달리 같은 이름의 애너테이션이 여러 번 적용될 수 있기 때문에 이 애너테이션들을 묶어주는
// 별도의 애너테이션 작성해야함
🔎 사용자 정의 애너테이션
➡️ 사용자가 직접 애너테이션을 정의해서 사용하는 것
@interface 애너테이션명 { // 인터페이스 앞에 @기호만 붙이면 애너테이션을 정의할 수 있습니다.
타입 요소명(); // 애너테이션 요소를 선언
}
➡️ 애너테이션은 다른 클래스나 인터페이스를 상속받을 수 없음
2. 람다(Lamda)
➡️ 람다식(Lamda Expression)은 함수형 프로그래밍 기법을 지원하는 자바의 문법요소
➡️ 메서드를 하나의 식으로 표현하여 코드를 매우 간결하면서 명확하게 표현 가능
➡️ 메서드를 더 간단하고 편리하게 표현하기 위해 고안된 문법 요소
🔎 기본 문법
//기존 메서드 표현 방식
void sayhello() {
System.out.println("HELLO!")
}
//위의 코드를 람다식으로 표현한 식
() -> System.out.println("HELLO!")
// 반환 타입과 메서드 이름 생략 가능
//--> 익명 함수(anonymous function) 부르기도 함
// 기존 방식
int sum(int num1, int num2) {
return num1 + num2;
}
// 람다식
(int num1, int num2) -> {
num1 + num2
}
// 특정 조건이 충족되면 람다식을 더욱 축약하여 표현 가능
// 메서드 바디에 실행문이 하나만 존재할 경우 중괄호와 return 문 생략가능, 세미콜론까지!
(int num1, int num2) -> num1 + num2
// 매개변수 타입을 함수형 인터페이스를 통해 유추할 수 있는 경우 매개변수 타입 생략가능
(num1, num2) -> num1 + num2
🔎 함수형 인터페이스
➡️ 자바에서는 함수는 반드시 클래스 안에서 정의되어야 하므로 메서드가 독립적으로 있을 수 없고 반드시 클래스 객체를 먼저 생성한 후 생성한 객체로 메서드를 호출해야 함
➡️ 즉 람다식 또한 객체, 이름이 없기 때문에 익명 객체라고 할 수 있음
익명 객체는 익명 클래스를 통해 만들 수 있는데 익명 클래스란 객체의 선언과 생성을 동시에 하여 오직 하나의 객체를 생성하고 단 한 번만 사용되는 일회용 클래스
// sum 메서드 람다식
(num1, num2) -> num1 + num2
// 람다식을 객체로 표현 --> 생성과 선언을 동시에 할 수 있음
new Object() {
int sum(int num1, int num2) {
return num1 + num1;
}
}
- 익명 객체를 생성하여 참조 변수 obj에 담아준다고 하더라도 sum 메서드를 사용할 수 있는 방법이 없음
→ 자바의 함수형 인터페이스(Functional Interface) 사용하여 해결할 수 있음 - 함수형 인터페이스(Functional Interface)는 단 하나의 추상 메서드만 선언가능
→ 람다식과 인터페이스의 메서드가 1:1로 매칭되어야 하기 때문에
- 함수형 인터페이스인 ExampleFunction에 추상 메서드 sum() 정의됨
→ 람다식을 참조할 참조 변수를 선언할 때 타입으로 사용하기 위해 필요함 - 함수형 인터페이스를 사용하면 참조 변수의 타입으로 함수형 인터페이스 사용하여 원하는 메서드에 접근가능
✍🏻 매개변수와 리턴값이 없는 람다식
@FunctionalInterface
interface MyFunctionalInterface {
void accept(); // 매개변수와 리턴값이 없는 추상 메서드를 가진 함수형 인터페이스
}
public class MyFunctionalInterfaceExample {
public static void main(String[] args) throws Exception {
// accept()가 매개변수를 가지지 않으니 람다식도 가지지 않음
MyFunctionalInterface example = () -> System.out.println("accept() 호출");
example.accept(); // 메서드 호출 --> {} 실행
}
}
// 출력값
accept() 호출
✍🏻 매개변수가 있는 람다식
@FunctionalInterface
public interface MyFunctionalInterface {
void accept(int x);
}
// 매개변수가 있고 리턴값이 없는 추상메서드를 가진 함수형 인터페이스
public class MyFunctionalInterfaceExample {
public static void main(String[] args) throws Exception {
MyFunctionalInterface example;
// accept()가 매개변수를 하나 가지기 때문에 람다식도 하나 가진다
example = (x) -> {
int result = x * 5;
System.out.println(result);
};
example.accept(2);
example = (x) -> System.out.println(x * 5);
example.accept(2);
}
}
// 출력값
10
10
✍🏻 리턴값이 있는 람다식
🔎 메서드 레퍼런스(참조)
➡️ 람다식에서 불필요한 매개변수를 제거할 때 주로 사용
➡️ 더욱더 간단하게 사용하기 위한 방법!
- 정적 메서드와 인스턴스 메서드 참조
클래스 :: 메서드
// 클래스 이름 뒤에 :: 기호 붙이고 정적 메서드 이름 기술
참조 변수 :: 메서드
// 객체를 먼저 생성한 다음 참조 변수 뒤에 :: 기호 붙이고 인스턴스 메서드 이름 기술
- 생성자 참조
➡️ 생성자를 참조한다는 것은 객체 생성을 의미
➡️ 단순히 객체를 생성하고 리턴하도록 구성된 람다식은 생성자 참조로 대치가능
➡️ 생성자가 오버로딩되어 여러 개 있으면 컴파일러는 함수형 인터페이스의 추상 메서드와 동일한 매개 변수 타입과 개수가 있는 생성자를 찾아 실행, 만약 생성자가 존재하지 않으면 컴파일 오류
(a,b) -> new 클래스(a,b)
//생성자 참조 문법
클래스 :: new
- 생성자 참조는 두 가지 방법 모두 같지만 실행되는 Member 생성자가 다름
내용이 많고 중요해서 길어졌다...... 제발 블로그 미루지 말자! 2탄으로 계속......
'SEB_BE_45 > 공부 정리' 카테고리의 다른 글
17. [Java] Thread, JVM, Garbage Collection / 20230508 (1) | 2023.05.09 |
---|---|
16. [Java] Annotation, 람다식, 스트림 / 20230503 (1) | 2023.05.09 |
15. [Java] 컬렉션(Collection) 2 / 20230502 (0) | 2023.05.03 |
14. [Java] 컬렉션 (Collection) 1 / 20230501 (0) | 2023.05.02 |
13. 객체지향프로그래밍의 심화 / 20230428 (0) | 2023.04.29 |