SEB_BE_43 / 23.01.06 회고

rse·2023년 1월 15일
0

코드스테이츠_BE_43

목록 보기
15/65

오늘 배우는 내용

  • 애너테이션
  • 람다
  • 스트림

애너테이션

애너테이션은 컴파일에 기록하는 것으로, 다른 기계에게 알려줄 때 쓰는 것이다.

여기 @Override 라고 적혀있는게 애너테이션이다.
@로 시작하고, 클래스, 인터페이스, 필드, 메서드 등에 붙여서 사용할 수 있다.

저렇게 @Override 를 써놓고 컴파일 과정에서 컴파일러가 @Override를 발견하면, @Override가 붙은 메서드와 동일한 이름을 가진 메서드가 상위 클래스(또는 인터페이스)에 존재하는지 검사한다. 즉, SuperClass에 example()이 존재하는지 검사한다.

만약, 상위 클래스(또는 인터페이스)에서 @Override가 붙어있는 메서드명과 동일한 이름의 메서드를 찾을 수 없다면 컴파일러가 컴파일 에러를 발생 시킨다.

왜 상위 클래스에 오버라이딩 메서드와 동일한 이름의 메서드가 존재하는지 확인해야할까?

개발자도 사람이기에 가끔 메서드의 이름이 잘 못 작성되는 경우가 있다.
그것을 방지하기 위해서 확인을 하는 것이다.

@Deprecated

@Deprecated는 기존에 사용하던 기술이 다른 기술로 대체되어 기존 기술을 적용한 코드를 더 이상 사용하지 않도록 유도하는 경우에 사용한다.



인스턴스화해서 사용하려고 하면 더 이상 사용이 안되는걸 확인할 수 있다.

Deprecated 애너테이션은 호환성 문제로 사용은 안하지만 둬야할 때 쓰인다고 한다.

@SuppressWarnings

컴파일 경고 메시지가 나오지않도록 한다. 경우에 따라서 경고가 발생할 것이 충분히 예상됨에도 묵인해야 할 때 주로 사용.

@SuppressWarnings 뒤에 괄호를 붙이고 그 안에 억제하고자 하는 경고메세지를 지정해줄 수 있다.

경고메세지를 다 외울필요는 없다. 그때 그때 필요하면 찾아서 쓰자.

@FunctionalInterface

함수형 인터페이스를 선언할 때, 컴파일러가 함수형 인터페이스의 선언이 바르게 선언되었는지 확인한다.

@FunctionalInterface
public interface ExampleInterface {
	public abstract void example(); // 단 하나의 추상 메서드
}

메타애너테이션

애너테이션을 정의하는 데에 사용되는 애너테이션으로, 애너테이션의 적용 대상 및 유지 기간을 지정하는데 쓰인다.


아까 정의한 @Override의 소스코드이다.
@Target
@Retention
가 붙어있는 것을 볼 수 있다.

@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;
}

@Documented

애너테이션의 정보가 문서에 포함되도록 하는 것.

@Inherited

하위 클래스가 애너테이션을 상속받도록 하는 것이다.
@Inherited 애너테이션을 상위 클래스에 붙이면, 하위 클래스도 상위 클래스에 붙은 애너테이션들이 동일하게 적용된다.

@Retention

애너테이션의 지속 시간을 정할 수 있다.

@Repeatable

애너테이션을 여러 번 붙일 수 있도록 허용한다.

람다

코드를 짧고 간결하면서 명확하게 표현할 수 있다.

함수형 프로그래밍을 지원해주는 문법이다.
함수형 프로그래밍은 무엇인가?
수학적 함수의 계산을 통해 자료를 처리하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임 중 하나
라고 한다. 그런데 이렇게 말하면 무슨 말인지 하나도 모르겠다. 솔직히.

그냥 코드가 명확해지고, 깔끔해질 수 있다. 라고 나는 받아들였다..

람다식은 -> 로 표현되고, 반환타입과 이름을 생략할 수 있다.

void hello() { System.out.println("Hello")
를 람다식으로 표현하면

() -> System.out.println("Hello");

메서드 레퍼런스

불필요한 메서드를 제거할 때 사용한다.

(left, right) -> Math.max(left, right)
지금 그냥 left, rigth를 전달만 해주고 있다는 것을 볼 수 있다.

Math::max
클래스이름 :: 메서드 이름

정적 메서드인 경우
클래스 :: 메서드
인스턴스 메서드인 경우
참조변수 :: 메서드

스트림

stream은 반복자 라는 뜻으로, 컬렉션이나 배열에서 사용할 수 있다.

정확하게 얘기하자면 배열 및 컬렉션의 저장 요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 하는 반복자. 라고 할 수 있겠다.

왜 사용하는가?

컬렉션이나 배열을 반복하려면 for, Iterator, while 을 사용해야 하는데 그렇게 되면 코드가 점점 길어지는 문제가 발생한다.
그걸 막기 위해 스트림을 사용한다.
또, 스트림을 사용하면 데이터 소스를 변경하지 않고 반복문을 돌 수 있다.

stream을 사용하면 복잡한 반복문을 간단하게 표현할 수 있다.

위 예시는 짝수를 filter로 걸러주고
peek를 통해 출력해준다
sum을 통해서 더해준다.

filter는 stream에서 괄호 안에 지정된 걸 걸러주는 역할을 한다.
peek 는 요소들을 순회하며 특정한 역할을 수행하고, sum은 값을 더해준다.

그런데 peek와 forEach는 요소들을 순회하며 특정한 작업을 수행한데 뭐가 다를까?
peek는 중간연산자로 몇번 사용 가능하지만 forEach는 최종 연산자로 마지막에 한번만 사용가능하다.

배열 스트림의 생성은 Arrays의 stream을 사용할 수도 있고, Stream의 of를 사용해서 만드는 것도 가능하다.

Stream<String> stream = Arrays.stream(arr);

Stream<String> stream = Stream.of(arr);

반면 컬렉션을 스트림으로 만드는 방법은 Collection에 정의된 stream 을 사용한다.
이유는 List, Set이 Collection으로부터 확장된 하위 클래스이기 때문.

List<Integer> list = Arrays.asList(1,2,2,4);
Stream<Integer> listStream = list.stream();

Stream 연습문제

주어진 list 각 요소에 2를 곱해 새로운 list를 만드는 문제

map 을 통해 연산을 해주고
collect toList를 통해서 새 리스트 만들어줌.

주어진 list를 합치고 중복된 이름은 제거하고 툭정 성씨 이름을 정렬한 후 list로 리턴.


concat을 사용하면 stream인 list를 합칠 수 있다.
startsWith을 사용하면 괄호 안에 있는 걸로 시작하는 문자를 찾아준다.

profile
기록을 합시다

0개의 댓글