TIL : 221201

JINSEON YE·2022년 12월 1일

TIL

목록 보기
25/88
post-thumbnail

인터페이스란?

  • 인터페이스 사이로 앞, 뒤의 개발 코드가 서로 통신하는 접점.
  • 극단적으로 동일한 목적 하에, 동일한 기능을 수행하게끔 보장하는 것이 인터페이스의 역할이자 개념! So, 앞, 뒤의 통신 규약 -> 동시에 개발 가능.
  • 결과 : 자바의 다형성을 극대화하여 개발코드 수정을 줄이고 프로그램 유지보수성을 높이기 위해 인터페이스를 사용한다.
  • 인터페이스가 정해지면 같이 개발 시작! 인터페이스가 수정이 되면 프론트도 알아야함. 인터페이스 = 의사소통이 꼭 필요함.

인터페이스 정리

  • 인터페이스는 interface 키워드를 통해 선언할 수 있으며 implements 키워드를 통해 일반 클래스에서 인터페이스를 구현할 수 있다.

  • 또한, JAVA8 이전까지는 상수, 추상메소드만 선언이 가능하지만,
    (상수, 추상메소드만 가능케했다는 것을 통해 그만큼 강제성이 강했다는 것을 유추할 수 있다.)
    JAVA8부터 디폴트메소드, 정적메소드가 추가되었다.
    (디폴트메소드, 정적메소드를 통해, 구현 강제성 안에 유연함을 심었다고 우선 이해하자)

인터페이스에서 정의하고 구현하는 4가지

public interface 인터페이스명 {

//상수

  • 인터페이스에서 값을 정해줄테니 함부로 바꾸지 말고 제공해주는 값만 참조해라 (절대적)

타입 상수명 = 값;

(final/static : 지우라고뜸) 타입 상수명(대문자 convention) = 값;
	String HI = "Hi~";
  • 변수명을 대문자_ 조합 Ex)HI_FIVE 으로 할 수 있음.
  • 상수가 아닌 일반 필드는 쓸 수 없음. (상수만)

//추상 메소드

  • 가이드만 줄테니 추상메소드를 오버라이팅해서 재구현해라. (강제적)

타입 메소드명(매개변수, ... );

List<String> findAllName();

-추상메소드를 오버라이드하지않으면 빨간줄이 뜸 -> 추상메소드로 구현체에게 구현 강제

//디폴트 메소드

  • 인터페이스에서 기본적으로 제공해주지만, 맘에 안들면 각자 구현해서 써라. (선택적)

default 타입 메소드명(매개변수, ... ){
//구현부

default 타입 메소드명(파라미터,...) {...}
default void printHi() {
	System.out.println(HI);
	}
  • 기본 메서드 제공
    • 자바는 단일 상속, 인터페이스는 다중 구현이 가능하다.
      → 충돌나는 메서드시그니처는 오버라이딩을 강제한다.
    • 오버라이딩 가능.
    • 기본 public . 생략시 컴파일 과정에서 붙음.
  • 이미 운영되고 있는 시스템에서 추가 요건으로 인해 불가피하게 반영을 해야할 때 디폴트메소드를 쓰면 효과적
    • 충돌나는 메서드시그니처는 오버라이딩을 강제한다. -> 그래서 다중구현이 지원됨

//정적 메소드

  • 인터페이스에서 제공해주는 것으로 무조건 사용 (절대적)

static 타입 메소드명(매개변수) {

//구현부

static void printHi() {
		System.out.println(HI);
	}
}
  • static 메소드를 쓸 수 있음. static block은 쓸 수 없음.

참고 : https://limkydev.tistory.com/197

추상클래스 :

다형성을 구현하는 기술

  • Promotion : 상속 또는 인터페이스의 자동 타입 변환 (자동,묵시적 형변환 : 데이터타입이 작은 것에서 큰 것으로 변환(업캐스팅)할 때 아무말 없이 변환해줌!)
  • 다운캐스팅은 데이터 손실이 있기 때문에, 개발자가 직접 명시적으로 어떤 코드를 작성해줘야함.
  • 숫자형 데이터타입에서 뿐만 아니라 객체에서도 프로모션이 일어남.

promotion memory구조 보기

  • 인터페이스 변수 = 구현객체; <- 자동 타입 변환

  • 상위 인터페이스, 클래스, 추상 클래스로 Upcasting 가능
    → 모든 클래스는 extends Object 가 생략되어있다. ->모든클래스는 오브젝트를 상속하고 있기 때문!! 알고있어야함.
    → 모든 클래스는 Object로 Upcasting 가능 (모든 클래스는 object를 상속하고 있음)
  • 클래스에서 업캐스팅 프로모션은 상위인터페이스 상위클래스에 대해 프로모션이 발생함 그래서 자연스럽게 위의 코드를 쓸 수 있는 것임.

인터페이스는 생성자가 없다.

clazz: 클래스 대신에 작성하는 팁 / Object anyClazz = new AnyThing();

- Static Dispatch : 컴파일 시점에서, 컴파일러가 특정 메소드를 호출할 것이라고 명확하게 알고있는 경우이다. (정적)

  • 컴파일 시 생성된 바이트 코드에도 이 정보가 그대로 남아있다.
  • 런타임(실행 시점)이 되지 않아도 미리 결정하는 개념이다.
  • 함수를 오버로딩하여 사용하는 경우 인자의 타입이나 리턴타입 등에 따라 어떤 메서드를 호출할지 알 수 있는 경우
public class ACar {
    public void print() { 
        System.out.println("A");
    }
}

public class BCar extends ACar { //메소드 오버라이딩 - ACar상속 후 함수 재정의
    public void print() {
        System.out.println("BCar");
    }
}

//ex) 스태틱 메서드 디스패치
public static void main(String[] args) {
    BCar bcar = new BCar();
    System.out.println(bcar.print());  //BCar를 출력.
}

- Dynamic Dispatch : 정적 디스패치와 반대로 컴파일러가 어떤 메서드를 호출하는지 모르는 경우이다. 동적 디스패치는 호출할 메서드를 런타임 시점에서 결정한다.

  • 인터페이스나 추상 클래스에 정의된 추상 메소드를 호출하는 경우다.
  • 인터페이스 또는 추상클래스로 선언하고 구현/상속 받은 하위 클래스의 인스턴스를 생성.
  • 컴파일러가 알고 있는 타입에 대한 정보를 토대로 런타임 시 해당 타입의 객체를 생성하고 메서드를 호출.
public interface Car {
    void print();
}

public class A implements Car {
    @Override
    public void print() {
        System.out.println("A");
    }
}

public class B implements Car {
    @Override
    public void print() {
        System.out.println("B");
    }
}

// 예시 1
public static void main(String[] args) {
   // 다이나믹 메소드 디스패치
   Car car = new A();
   System.out.println(animal.print());
}

런타임 전에는 객체 생성이 되지 않기 때문에 Car car = new A();를 해도, 컴파일러는 A가 생성됨을 알 수 없으므로 Car가 정의한 print() 메소드만 접근 가능.

참고 : https://doompok.tistory.com/21

Dynamic Dispatch 동작

자바는 Promotion으로 Upcasting된 객체의 메소드를
런타임 시점에서 오버라이딩된 메소드에 대한 호출이 확인된다. → Dynamic Method Dispatch -> 실시간 다형성

  • 메커니즘 이해

오버라이딩(Overriding)

상위 클래스의 메서드와 이름과 용례(signature)가 같은 함수를 하위 클래스에 재정의하는 것을 말한다
즉, 상속 관계에 있는 클래스 간에 같은 이름의 메서드를 정의하는 것을 말한다.

public abstract class Shape {
  public void printMe() { System.out.println("Shape"); }
  public abstract double computeArea();
}
public class Circle extends Shape {
  private double rad = 5;
  @Override // 개발자의 실수를 방지하기 위해 @Override(annotation) 쓰는 것을 권장
  public void printMe() { System.out.println("Circle"); }
  public double computeArea() { return rad * rad * 3.15; }
}
public class Ambiguous extends Shape {
  private double area = 10;
  public double computeArea() { return area; }
}
https://gmlwjd9405.github.io/2018/08/09/java-overloading-vs-overriding.html

Circle에서 printMe() 메서드를 재정의한다.
오버라이딩을 할 때 개발자의 실수를 방지하기 위해 메서드 위에 @Override 를 관례적으로 적는다.
참고 : https://gmlwjd9405.github.io/2018/08/09/java-overloading-vs-overriding.html

함수형 인터페이스와 람다 표현식

함수형 인터페이스

  • 추상 메소드를 하나만 가지고 있는 인터페이스

  • 자바는 클래스없이 메소드를 구현할 수 없기 때문에 이런 함수형 인터페이스라는 개념을 도입해서 함수를 쉽게 사용할 수 있게 함.

  • @FuntionalInterface 에노테이션을 선언하면 컴파일 시점에서 추상메소드를 하나만 갖는지 체크해줌.
    -> 컴파일 시 추상메소드가 하나일 수 있도록 강제해줌.

@FunctionalInterface
public interface Sum {
    int intSum(int x, int y);
}

에노테이션 : 인터페이스 위에 골뱅이 붙은 애들 ex) @FuntionalInterface

람다 표현식 (Lambda Expression)

  • 함수형 인터페이스를 더 짧게 표현할 수 있게 도와주는 것
  • 메소드를 하나의 식으로 표현한 것.
    람다식은 익명 객체라고 했다.

➕ 익명 객체란?
프로그램에서 일시적으로 한번만 사용되고 버려지는 객체
-나중에 다시 불러질 이유가 없다는 뜻
사용 이유 : 1. 프로그램 내에서 일시적으로(단발성으로) 한번만 사용되어야 하는 객체일 경우
-> UI 이벤트처리, 스레드 객체 등 (단발성 이벤트 처리)
2. 재사용성이 없고, 확장성을 활용하는 것이 유지보수에서 더 불리할 때
-> 비즈니스 로직이 정말 재각각이며, 재사용성이 전혀없어 매번 클래스를 생성해야하는 비용이 더 많을때
참고 : https://limkydev.tistory.com/226

int sum(int x, int y) {
	return x + y;
}

-> 람다 표현식으로 바꾼다면

(x, y) -> x + y;

-> 람다는 리턴이 없어도 리턴이 됨

Deprecated : 더이상 지원되지 않는다.

메소드레퍼런스

아래 코드에서 인텔리제이가 추천해주는 코드로 변경하는 것

@FunctionalInterface
public interface Sum {
    int intSum(int x, int y);
}

...
import ...Sum;

public class Main { 
    Sum sum = (a, b) -> a + b;

    System.out.println(sum.intSum(1, 2));
}

...
import ...Sum;

public class Main { 
	  Sum sum = Integer::sum;

    System.out.println(sum.intSum(1, 2));
}

로 바꾸라고 추천해줌 ( (a, b) -> a + b; -> Integer::sum;)

스트림

  • 배열이나 컬렉션에 담긴 데이터를 다룰 때, 반복문이나, iterator를 사용하면 코드가 길어지고, 가독성이 떨어진다.
    -> call back hell : 아도겐코드 : for문 if문의 엄청난 중첩

해결책 : Stream API

  • 스트림은 원본 데이터를 변경하지 않는다 -> immutable
  • 스트림은 재사용이 불가 -> 최종 연산이 실행된 후 재사용 불가.

스트림 파이프라인 : 0~N개의 중개 연산과 1개의 종료 연산으로 구성

파이프라인 : 인련의 과정, 순서, 작업 순서가 중요

  • 중개연산 : 스트림을 리턴.
  • 종료연산 : 스트림을 리턴하지 않는다.
    -> 종료연산을 하지 않으면 파이프라인이 끝나지 않은 것이다.
  1. 대표 스트림 연습 (스트림 도구들!)
    • 중개 연산자
      • 필터링 : filter , distinct
      • 변환 : map , flatMap
      • 제한 : limit , skip
      • 정렬 : sorted
    • 최종 연산
      • 요소 출력 : forEach
      • 요소 검색 : findFirst, findAny
      • 요소 통계 : count, min, max
      • 요소 연산 : sum, average
      • 요소 수집 : collect

optional

옵셔널 등장 배경 : 메소드에서 리턴을 널처리 하지않고 널포인트 익셉션이 발생하지 않도록 노력한 자바 아키텍쳐들의 노력의 결과물

  • Optional이 제공하는 메소드로 NPE(Null Pointer Exception) 예외를 간단하게 회피할 수 있다.
  • Wrapper Class이다.

Wrapper Class : 기본 자료타입(primitive type)을 객체로 다루기 위해서 사용하는 클래스들을 래퍼 클래스(wrapper class)라고 합니다.

참고 : https://coding-factory.tistory.com/547

  • Optional의 객체 꺼내는 방법
Optional<String> opt = Optional.ofNullable("Optional은 Wrapper Class");

System.out.println(opt.get());
  • null 을 반환하면 오류가 발생할 가능성이 매우 높은 경우에
    “결과 없음”을 명확하기 드러내기 위해 메소드의 반환 타입으로 사용되도록 매우 제한적인 경우로 설계됨.
    -> 옵셔널은 메소드의 반환타입으로만 사용해야한다.

⚠️ 잘못 사용하면 오히려 코드가 오히려 지저분해지고, 의미 없는 동작, Side-Effect 유발이 많이 발생할 수 있음.

  • NPE은 물론이고, NoSuchElementException이 발생함.
  • 잘못된 Optional 사용으로 새로운 문제들이 발생함.
  • 코드의 가독성을 파괴
  • 시간, 공간적 비용이 증가함

기타 정보

리스트의 경우 for문을 사용해서 어떤 값을 뽑아내고 그 값을 변수를 하나 설정해서 add해주고 그 값을 뽑아내서-??

  • 값스트림을 쓰는 이유
    : 병렬처리가 가능함 / 병렬처리 : 여러가지 일을 한 번에 수행하는 것
    : 유지보수가 쉽다.

  • 문자열 붙이는 방법:
    -String : 문자열 연산이 적고 멀티쓰레드 환경일 경우
    -StringBuffer : 문자열 연산이 많고 멀티쓰레드 환경일 경우
    -StringBuilder : 문자열 연산이 많고 단일쓰레드이거나 동기화를 고려하지 않아도 되는 경우 -> 연산이 적은 경우 적은 데이터만 가지고 쓰니까 빨라짐 시간복잡도 내려감

그렇다면 동일한 API를 가지고 있는 StringBuffer, StringBuilder의 차이점은 무엇일까요?

  • 가장 큰 차이점은 동기화의 유무로써 StringBuffer는 동기화 키워드를 지원하여 멀티쓰레드 환경에서 안전하다는 점(thread-safe) 입니다. 참고로 String도 불변성을 가지기때문에 마찬가지로 멀티쓰레드 환경에서의 안정성(thread-safe)을 가지고 있습니다.
  • 포함관계 : Ex) reservation.userPhone '.'으로
    파일경로처럼 불러오기

  • 메인 내부에서 다 끝나면 종료시키는 것.
    sys.exit(0); : 정상종료
    sys.exit(1); : 강제종료

  • 클래스간의 포함관계


Circle circle = new Circle(); → circle.(Point)c.x or circle.(Point)c.y

포함관계 : ‘~은~을 가지고 있다.(has-a)’
상속관계 : ‘~은~을 이다.(is-a)’ extends

원(Circle)은 점(Point)이다. - Circle is a point.
원(Circle)은 점(Point)을 가지고있다. - Circle has a Point.
90%이상이 포함 관계이다.! 상속은 제약이 있어 꼭 필요 할 때만.
잘 모르면 포함으로 진행해라. 대부분 포함이다.

profile
백엔드 개발자

0개의 댓글