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. 내부클래스

- 일반 중첩 클래스(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
}
}