카테고리 없음

2024. 7. 16. (화) 슈퍼코딩 부트캠프 Day 38 / 주특기 4주차

태영9922 2024. 7. 16. 13:56

 

1.클래스 아쉬운점

  • 매번 인스턴스화 해야할까? -> java static 활용
  • 여러 클래스, 메소드 유사한 걸 표현하려면 상속해야만 할까? -> 내부클래스 문법 활용하여 개념적 영역으로 그룹화
  • 클래스를 한번만 사용하고 싶은데 새로 클래스를 정의해야 할까? -> 내부 클래스 문법 활용

 

2. Java 유틸리티 클래스

  • 인스턴스화가 필요 없음.
  • 대표 예시 : Math, Arrays 등
  • Math.max(), Arrays.sort() 등
  • 속성과 메소드 모두 static으로 선언해야함 -> 인스턴스화 하지 않고 사용할 수 있음
//유틸클래스 정의
public class StringUtils {
    public static boolean isEmpty(String str) {
        if (str == null || str.isEmpty()) {
            return true;
        } else return false;
    }

    public static String reverse(String str) {
        StringBuilder sb = new StringBuilder();
        for (int i = str.length() - 1; i >= 0; i--) {
            sb.append(str.charAt(i));
        }
        return sb.toString();
    }

    public static int countChar(String str, char target) {
        int count = 0;
        for (int i = 0; i < str.length(); i++) {
            if (str.charAt(i) == target) {
                count++;
            }
        }
        return count;
    }

    public static boolean containsChar(String str, char target) {
        return countChar(str, target) >= 1;
    }
}
 
//유틸클래스 사용

public class StringUtilTest {
    public static void main(String[] args) {
        String str = "Hello, World";
        char target = 'o';

        boolean isEmpty = StringUtils.isEmpty(str);
        System.out.println("is str Empty? " + isEmpty);

        String str2 = StringUtils.reverse(str);
        System.out.println("reversed str2: " + str2);

        int countChar = StringUtils.countChar(str, target);
        System.out.println(target + "이 몇개 있는지? " + countChar);

        boolean isContainChar = StringUtils.containsChar(str, target);
        System.out.println(target + "이 포함되어 있는지? " + isContainChar);

    }
}
 

3. 내부클래스

출처 :  https://sjh836.tistory.com/145

 

  • 일반 중첩 클래스(InnerClass) : 클래스 안에 클래스 정의 -> 외부 클래스 인스턴스와 연관
OuterClass outerClass = new OuterClass(20);
OuterClass.InnerClass innerClass = outerClass.new InnerClass(10); 
//외부 클래스를 인스턴스화 한 이후에 외부 클래스에서 new로 내부클래스 생성 필요

innerClass.display();
 
  • 정적 중첩 클래스(StaticNestedClass) : 클래스 안에 static 클래스 정의 -> 외부 클래스 인스턴스와 독립적
OuterStaticClass outerStaticClass = new OuterStaticClass(30);
OuterStaticClass.InnerStaticClass innerStaticClass = new OuterStaticClass.InnerStaticClass(40);
//외부클래스 생성과 상관 없이 내부클래스에서 직접 new로 생성 가능

innerStaticClass.display();
 

  • 지역 내부 클래스 (= local Class) -> 여러번 인스턴스화 가능
//지역 내부 클래스
class LocalWalk implements Walkable {
@Override
public void walk() {
    System.out.println("LocalWalk : Walking");
    }
}

LocalWalk localWalk = new LocalWalk();
localWalk.walk();
 
  • 익명 내부 클래스(Annoymous InnerClass) -> 한번만 인스턴스화 가능
 int i = 0;
//익명 내부 클래스
Walkable annoymousWalk = new Walkable() {
@Override
public void walk() {
        i = 100; //접근은 가능하지만 쓰기는 불가능
        System.out.println(i); 
        System.out.println("AnnoymousWalk : Walking");
    }
};
annoymousWalk.walk();
 

1. 함수형 프로그래밍

2. 람다식 문법 : 람다식은 익명 내부클래스로 구현되어 돌아감

@FunctionalInterface
public interface StringNum {
    void printString(String str, int num);
}

@FunctionalInterface //메소드 하나만 정의 가능
public interface MultipleNum {
    int calculate(int num);
}
 
public class LambdaTest1 {
    public static void main(String[] args) {
        MultipleNum multipleNum1 = (x) -> x * 1; //인터페이스 메소드를 오버라이드
        MultipleNum multipleNum2 = (i) -> i * 2;
        MultipleNum multipleNum3 = (x) -> {
            return x * 3;
        };
        MultipleNum multipleNum4 = (int myInt) -> {
            return myInt * 4;
        };

        System.out.println(multipleNum1.calculate(5));
        System.out.println(multipleNum2.calculate(5));
        System.out.println(multipleNum3.calculate(5));
        System.out.println(multipleNum4.calculate(5));

        StringNum stringNum1 = (x, y) -> System.out.println(x);
        StringNum stringNum2 = (x, y) -> {
            for (int i = 0; i < y; i++)
                System.out.println(x);
        };
        StringNum stringNum3 = (String x, int y) -> {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < y; i++)
                sb.append(x);
            System.out.println(sb);
        };
        stringNum1.printString("hello", 5);
        stringNum2.printString("hello", 5);
        stringNum3.printString("hello", 5);
    }
}
 

 

3. 람다식 활용

  • 함수 인자로 람다식 활용하기
public class LambdaUseTest {
    public static void main(String[] args) {
        printNum((x) -> x * 100); //output : 500
    }

    public static void printNum(MultipleNum multipleNum) {
        System.out.println(multipleNum.calculate(5));
    }
}
 
  • Generin Programming 작성
@FunctionalInterface
public interface GenericLambda<T> {
    T calculate(T t);
}
 
public class LambdaUseTest {
    public static void main(String[] args) {
        GenericLambda<String> genericLambda1 = (str) -> str.toUpperCase();
        GenericLambda<Integer> genericLambda2 = (num) -> num * 10;
        GenericLambda<Boolean> genericLambda3 = (myBool) -> myBool & true;

        System.out.println(genericLambda1.calculate("hello")); //output : HELLO
        System.out.println(genericLambda2.calculate(5)); //output : 50
        System.out.println(genericLambda3.calculate(true)); //output : true
    }

    public static void printNum(MultipleNum multipleNum) {
        System.out.println(multipleNum.calculate(5));
    }
}
 
  • Optional 사용
import java.util.Optional;

public class OptionalLambdaTest {
    public static void main(String[] args) {
        String str = null;
        Optional<String> stringOptional = Optional.ofNullable(str);

        String str2 = stringOptional.orElseGet(() -> "default").toUpperCase();
                                               //인자로 람다식 사용
        System.out.println(str2); //str이 null이기 때문에 orElseGet에 걸려서 DEFAULT 출력
    }
}
 

1. Stream API

  • Stream API란? 함수형 프로그래밍을 도입하여 컬렉션, 배열 등을 처리/조작을 효율적으로 하는 API
  • 왜 사용하는가? 가독성 향상, 병렬연산 가능(성능 향상)
  • Stream 문법
package exercise.chapter_55;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamTest1 {
    public static void main(String[] args) {
        //Stream.of , Arrays.Stream, Collection(List) -> Stream

        Stream<String> fruits = Stream.of("apple", "banana", "orange");
        Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        //Arrays.Stream 생성
        Stream<String> fruits2 = Arrays.stream(new String[]{"apple", "banana", "orange"});
        Stream<Integer> integerStream2 = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

        //List.Stream
        List<String> fruitsList = new ArrayList<>(); //String 리스트 생성
        fruitsList.add("apple");
        fruitsList.add("banana");
        fruitsList.add("orange");
        fruitsList.add("mango");
        fruitsList.add("grapes");

        Stream<String> fruit3 = fruitsList.stream();

        List<Integer> integerList = new ArrayList<>();
        integerList.add(1);
        integerList.add(2);
        integerList.add(3);
        integerList.add(4);
        integerList.add(5);
        integerList.add(6);
        integerList.add(7);
        integerList.add(8);
        integerList.add(9);
        integerList.add(10);

        Stream<Integer> integerStream3 = integerList.stream();

        for (String fruit : fruitsList) {
            System.out.println("for문 : " + fruit.toUpperCase());
        }

        //Stream을 반복문 돌리기
        fruit3.forEach((fruit) -> System.out.println("forEach : " + fruit.toUpperCase()));


        for (Integer integer : integerList) {
            if (integer % 2 == 0) System.out.println("even : " + integer);
        }

        integerStream3.filter((i) -> i % 2 == 0)
                .filter((i) -> i > 4) //filter 여러개 적용 가능
                .forEach(i -> System.out.println("filter : " + i));

        //integerStream3.forEach(i -> System.out.println("forEach : " + i.toString()));
        //Stream은 일회용임. IllegalStateException 예외 발생
        //intgerStream3를 만든 integerList는 그대로 살아 있음

        for (Integer integer : integerList) {
            if (integer % 2 == 0) System.out.println("다시 출력 even : " + integer);
        }
    }
}
 
  • Stream : 생성 -> 중간연산(filter 등)/여러개 사용 가능 -> 최종연산 /한개만 사용 가능
  • Stream은 source로 사용한 배열, List등 기존 자료 변형 없음
  • Stream은 최종연산해서 한번 끝나면 재사용 불가

 

2. StreamAPI 연산

  • 대표적인 최종 연산 : forEach(), collect(), findFirst(), sum(), average(), count, max, min(), reduce() 등
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

public class StreamTerminalOpsTest {
    public static void main(String[] args) {
        List<String> fruits = new ArrayList<>();
        fruits.add("Apple");
        fruits.add("Apple");
        fruits.add("Apple");
        fruits.add("Banana");
        fruits.add("Orange");
        fruits.add("Mango");
        fruits.add("Grapes");
        fruits.add("WaterMelon");
        fruits.add("Pineapple");
        fruits.add("Strawberry");

        //1. forEach() : 출력에 많이 사용
        fruits.stream().forEach((fruit) -> System.out.println("forEach : " + fruit));
//        output
//        forEach : Apple
//        forEach : Apple
//        forEach : Apple
//        forEach : Banana
//        forEach : Orange
//        forEach : Mango
//        forEach : Grapes
//        forEach : WaterMelon
//        forEach : Pineapple
//        forEach : Strawberry

        //2. collect() : 다른 또는 같은 컬렉션 반환
        Set<String> fruitSet = fruits.stream().collect(Collectors.toSet());
        System.out.println("Set : " + fruitSet);
        //output : Set : [Apple, Grapes, Strawberry, Mango, WaterMelon, Pineapple, Orange, Banana]

        //3. findFirst() : Stream의 첫번째 값 산출, Optional
        Optional<String> fruitOptional = fruits.stream().findFirst();
        System.out.println("Optional : " + fruitOptional.orElseGet(() -> "기본 과일"));
        //output : Optional : Apple

        List<Integer> integers = new ArrayList<>();
        integers.add(1);
        integers.add(2);
        integers.add(3);
        integers.add(4);
        integers.add(5);

        //4. sum, average -> mapToInt 필요
        System.out.println("sum of stream values : " + integers.stream().mapToInt((i) -> i).sum());
        System.out.println("avg of stream values : " + integers.stream().mapToInt((i) -> i).average().getAsDouble());
//        output
//        sum of stream values : 15
//        avg of stream values : 3.0

        //5. count, max, min
        System.out.println("length of stream : " + integers.stream().count());
        System.out.println("max of stream values : " + integers.stream().mapToInt((i) -> i).max().getAsInt());
        System.out.println("min of stream values : " + integers.stream().mapToInt((i) -> i).min().getAsInt());
//        output
//        length of stream : 5
//        max of stream values : 5
//        min of stream values : 1

        //6. reduce
        int result = integers.stream().reduce(0, (a, b) -> a + b);
        System.out.println("result : " + result);
//        output
//        result : 15


    }
}