Java심화_실시간 2일차

5w31892p·2022년 12월 1일
0

Java_실시간

목록 보기
6/7

미니프로젝트 코드 개선해보기
1. 스트림 이용해 리스트 조회 코드 개선
2. Optional 이용해 Null Safety 코드로 개선


keyword

Promotion | Object
Dynamic Dispatch
익명 객체 | 메소드 레퍼런스
Immutable / Mutable
Stream
Optional


체크리스트

✔ 인터페이스 내용복습
✔ Promotion 과 Dynamic Dispatch를 설명 가능?
✔ 함수형 인터페이스와 람다 이해
✔ 스트림 활용 가능?
✔ Optional 사용 가능?
✔ 미니프로젝트 개선 가능?


📜 인터페이스

  • 의사소통의 창구
  • 앞 뒤 개발 코드가 서로 통신하는 접점
  • 앞 뒤 통신 규약 -> 동시 개발 가능
  • 프론트, 백 동시 작업 가능
interface Practice {
	// 상수
	(final/static : 지우라고뜸) 타입 상수명(대문자 convention) =;
	String HI = "Hi~";

	// 추상 메서드
	List<String> findAllName();

	// Default 메소드
	default 타입 메소드명(파라미터,...) {...}
	default void printHi() {
		System.out.println(HI);
	}

	// static 메소드
	static void printHi() {
		System.out.println(HI);
	}
}

Interface

  • 상수만 가능 <- 클래스 로드 시점에서 초기화
    -> public static final이 컴파일 과정에서 붙음
  • 추상 메소드로 구현체에게 구현 강제
  • 기본 메소드 제공
    • 자바는 단일 상속
    • 인터페이스는 다중 구현이 가능
      -> 충돌나는 메소드시그니처는 오버라이딩을 강제
    • 오버라이딩 가능
    • 기본 public 생략시 컴파일 과정에서 붙음
  • static 메소드
    -> 헬퍼 또는 유틸리티 메소드를 제공할 때 사용
    • static 블록 작성 불가

:: 📜 추상 클래스 VS 인터페이스

역사 읽으러 gogo


:: 📜 다형성을 구현하는 기술

다형성(polymorphism)이란?

하나의 객체가 여러가지 타입을 가질 수 있는 것
자바에서는 부모 클래스의 참조 변수로 자식 클래스의 인스턴스를 참조할 수 있도록 구현
상속, 추상화와 더불어 객체지향프로그래밍 중요한 특징

상속을 통해 기능을 확장하거나 변경하는 것을 가능하게 해줌
같은 클래스 내에 코드 길이를 줄여줌
인터페이스는 객체로 생성할 수 없기 떄문에 생성자 없음
추상클래스는 생성자 있음
  • 상속 또는 인터페이스의 자동 타입 변환 (Promotion)

:: Promotion Memory 구조 보기

  • 인터페이스 변수 = 구현객체; <- 자동 타입 변환
    Interface clazz = new InterfaceImplementClass();

상위 인터페이스, 클래스, 추상클래스로 Upcasting 가능

-> 모든 클래스는 extends Object 생략
-> 모든 클래스는 Object로 Upcasting 가능

Object anyClazz = new AnyThing();

:: Dynamic Dispatch 동작

Dynamic Method Dispatch -> 실시간 다형성

다형성 -> 오버라이딩 구현

오버라이딩이란?

상위 클래스에 선언되어 있는 메소드를 하위 클래스에서 동일하게 선언하여 사용하는 것
슈퍼클래스를 상속받은 서브클래스에서 슈퍼클래스의 
(추상)메소드를 같은 이름, 같은 반환값, 같은 인자로 메소드 내의 로직들을 새롭게 정의하는 것

오버로딩이란?

하나의 클래스에서 같은 이름의 메소드들을 여거래 가질 수 있게 하는 것
단, 메소드 인자들은 달라야 함 (반환 타입은 같아도 되고, 달라도 됨)

Promotion으로 Upcasting된 객체 메소드를
런타임 시점에서
오버라이딩된 메소드에 대한 호출 확인

매커니즘 이해

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

:: 함수형 인터페이스

  • 추상 메소드 하나만 가지고 있는 인터페이스
  • @FuntionalInterface 애노테이션을 인터페이스에 선언하면 컴파일 시점에서 추상메소드를 하나만 갖는지 체크해줌
@FunctionalInterface
public interface Sum {
    int intSum(int x, int y);
}
애노테이션이란 ?

상단에 @으로 시작하게 쓴 것

:: 람다 표현식 (Lambda Expression)

  • 간단히 말해 메소드를 하나의 식으로 표현한 것
  • 자바에서 람다식은 익명 객체
  • 람다식 다루기 위해서는 참조 변수 필요
  • Object로는 람다를 다룰 수 없어서 나온 것이 함수형 인터페이스
int sum(int x, int y) {
	return x + y;
}

// 람다 표현식으로 변경

(x, y) -> x + y;

메소드 레퍼런스 (Method Reference)

  • 람다 표현식이 단 하나의 메소드만을 호출하는 경우에 해당 람다 표현식에서 불필요한 매개변수를 제거하고 사용할 수 있도록 도움
  • 메소드 레퍼런스를 사용하면 불필요한 매개변수 제거 후 :: 기호를 사용해 표현함
...
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));
}

📜 스트림

배열이나 컬렉션에 담긴 데이터를 다룰 때, 반복문이나, iterator를 사용하면 코드가 길어지고, 가독성이 떨어지는 것을 해결하기 위해 Stream API 등장

  • 특징
    • 데이터 변경 안함 -> Immutable
    • 재사용 불가 -> 최종 연산 실행된 후 재사용 불가
  • 스트림 파이프라인
    • 0~N개의 중개 연산과 1개의 종료 연산으로 구성
    • 칙칙폭폭 순서 지켜라
  • 중개연산
    • Stream 리턴
  • 종료연산
    • Stream 리턴 안함

:: 대표 스트림

:: 중개 연산자

  • 필터링 : filter , distinct
  • 변환 : map , flatMap
  • 제한 : limit , skip
  • 정렬 : sorted

:: 최종 연산

  • 요소 출력 : forEach
  • 요소 검색 : findFirst, findAny
  • 요소 통계 : count, min, max
  • 요소 연산 : sum, average
  • 요소 수집 : collect

📜 Optional

객체를 감싸는 것! ----> 즉, 객체를 쌈싸는 것

NPE(Null Pointer Exception) 예외를 Optional이 제공하는 메소드로 간단하게 회피 가능

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

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

null을 반환하면 오류가 발생할 가능성이 매우 높은 경우,
“결과 없음”을 명확하기 드러내기 위해
메소드의 반환 타입으로 사용되도록 매우 제한적인 경우로 설계

  • Java 언어 아키텍트 Brian Goetz

Optional은 메소드 반환 타입으로만 사용해야함

  • Optional은 Wrapper Class (래퍼 클래스)

래퍼 클래스(Wrapper class)

  • 기본 타입에 해당하는 데이터를 객체로 포장해 주는 클래스
  • 각각의 타입에 해당하는 데이터를 인수로 전달받아, 해당 값을 가지는 객체로 만들어줌
  • 모두 java.lang 패키지에 포함되어 제공

기본 타입래퍼 클래스
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

래퍼 클래스 중에서 Integer 클래스와 Character 클래스만이 자신의 기본 타입과 이름이 다름

Optional이 객체 꺼내는 방법

Optional<String> opt = Optional.ofNullable("Optional은 Wrapper Class");

System.out.println(opt.get());

✍ 백문이불여일타

:: Optional

참고사이트

:: empty()

Optional<String> empty = Optional.empty();

System.out.println(empty.isPresent()); // false

:: of()

Optional<String> empty = Optional.of("assert NotNull");

System.out.println(empty.isPresent()); // true
Optional.of(null); // NPE 발생. of는 null이 아님을 확신할때 사용

:: ofNullable()

Optional<String> empty = Optional.ofNullable(null);

System.out.println(empty.isPresent()); // false

:: ifPresent()

  • Optional에서 꺼낸 객체가 존재한다면, 구문수행
String name = null;
Optional<String> opt = Optional.ofNullable(name);

opt.ifPresent(n -> System.out.println(n.length()));

:: orElse()

  • Optional에서 꺼낸 객체가 존재한다면 꺼내고, 그렇지 않다면?
  • orElse의 인자값을 반환
System.out.println(Optional.ofNullable(null).orElse("냐옹"));

System.out.println(Optional.ofNullable("Hey!").orElse("냐옹")); 

// 냐옹 
//Hey!

:: orElseGet()

  • orElse()와 비슷하지만, 인자값으로 람다 표현식의 결과값을 출력
System.out.println(Optional.ofNullable(null).orElseGet(String::new));
System.out.println(Optional.ofNullable(null).orElseGet(() -> "냐옹"));
System.out.println(Optional.ofNullable("Hey!").orElseGet(() -> "냐옹"));

// 냐옹 
//Hey!

:: orElseThrow()

  • Optional에서 꺼낸 객체가 존재한다면 꺼내고, 그렇지 않다면?
  • Exception 던지기
String nullName = null;
String name = Optional.ofNullable(nullName).orElseThrow(
        IllegalArgumentException::new);
        
// Exception

:: Stream

참고사이트

:: filter + anyElement / firstElement

List<String> elements = Stream.of("a", "b", "c")
										.filter(element -> element.contains("b"))
                    .collect(Collectors.toList());

    Optional<String> anyElement = elements.stream().findAny();
    System.out.println(anyElement.orElse("암것두 없어"));

    Optional<String> firstElement = elements.stream().findFirst();
    System.out.println(firstElement.orElse("한개두 없어"));

// b
// b

:: N개의 중개 연산자

Stream<String> onceModifiedStream = Stream.of("abcd", "bbcd", "cbcd");

Stream<String> twiceModifiedStream = onceModifiedStream
										.skip(1)
										.map(element -> element.substring(0, 3));

System.out.println(twiceModifiedStream); //?
System.out.println(twiceModifiedStream.collect(Collectors.toList()));

// java.util.stream.ReferencePipeline$3@816f27d
// [bbc, cbc]

:: map 활용

class Product {
    private int age;
    private String name;

    public Product(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }
}
List<Product> productList = Arrays.asList(
								new Product(23, "potatoes"),
                new Product(14, "orange"), new Product(13, "lemon"),
                new Product(23, "bread"), new Product(13, "sugar")
);

List<String> collectorCollection = productList.stream()
				.map(Product::getName)
				.collect(Collectors.toList());

System.out.println(collectorCollection);

double averageAge = productList.stream()
                .collect(Collectors.averagingInt(Product::getAge));
System.out.println("나이 평균 : " + averageAge);

int summingAge = productList.stream().mapToInt(Product::getAge).sum();
        System.out.println("나이 총합 : " + summingAge);
        
// [potatoes, orange, lemon, bread, sugar]
// 나이 평균 : 17.2
// 나이 총합 : 86

0개의 댓글