14-1. 내부 클래스 inner class
- 클래스 내부에 선언한 클래스로 이 클래스를 감싸고 있는 외부 클래스와 밀접한 연관이 있는 경우가 많고, 다른 외부 클래스에서 사용할 일이 거의 없는 경우에 내부 클래스로 선언해서 사용함
- 중첩 클래스라고도 함
- 내부 클래스의 종류 : 인스턴스 내부 클래스, 정적(static) 내부 클래스, 지역(local) 내부 클래스, 익명(anonymous) 내부 클래스
인스턴스 내부 클래스
- 내부적으로 사용할 클래스를 선언 (private으로 선언하는 것을 권장)
- 외부 클래스가 생성된 후 생성됨 (정적 내부클래스와 다름)
- private이 아닌 내부 클래스는 다른 외부 클래스에서 생성할 수 있음
정적 내부 클래스
- 외부 클래스 생성과 무관하게 사용할 수 있음
- 정적 변수, 정적 메서드 사용
지역 내부 클래스
- 지역 변수와 같이 메서드 내부에서 정의하여 사용하는 클래스
- 메서드의 호출이 끝나면 메서드에 사용된 지역변수의 유효성은 사라짐
- 메서드 호출 이후에도 사용해야 하는 경우가 있을 수 있으므로 지역 내부 클래스에서 사용하는 메서드의 지역 변수나 매개 변수는 final로 선언됨
익명 내부 클래스
- 이름이 없는 클래스 (위 지역 내부 클래스의 MyRunnable 클래스 이름은 실제로 호출되는 경우가 없음)
- 클래스의 이름을 생략하고 주로 하나의 인터페이스나 하나의 추상 클래스를 구현하여 반환
- 인터페이스나 추상 클래스 자료형의 변수에 직접 대입하여 클래스를 생성하거나 지역 내부 클래스의 메서드 내부에서 생성하여 반환 할 수 있음
- widget의 핸들러에 활용됨
14-2. 람다식 (Lambda expression)
함수형 프로그래밍과 람다식
- 자바는 객체 지향 프로그래밍 : 기능을 수행하기 위해서는 객체를 만들고 그 객체 내부에 멤버 변수를 선언하고 기능을 수행하는 메서드를 구현
- 자바 8부터 함수형 프로그래밍 방식을 지원하고 이를 람다식이라 함
- 함수의 구현과 호흡만으로 프로그래밍이 수행되는 방식
- 함수형 프로그래밍(Functional Programming : FP)
함수형 프로그래밍은 순수함수를 구현하고 호출함으로써 외부 자료에 부수적인 영향을 주지 않도록 구현하는 방식.
순수 함수란 매개변수만을 사용하여 만드는 함수
즉, 함수 내부에서 함수 외부에 있는 변수를 사용하지 않아 함수가 수행되더라도 외부에는 영향 주지 않
함수를 기반으로 하는 프로그래밍이고 입력받는 자료 이외에 외부 자료를 사용하지 않아 여러 자료가 동시에 수행되 는 병렬 처리가 가능
함수형 프로그래밍은 함수의 기능이 자료에 독립적임을 보장
동일한 자료에 대해 동일한 결과를 보장
람다식 문법
- 익명 함수 만들기
- 매개 변수와 매개변수를 이용한 실행문 (매개변수) -> (실행문;)
- 두 수를 입력 받아 더하는 add() 함수 예제
int add(int x, int y) { return x+y; } |
- 람다식으로 표현
(int x, int y) -> {return x+y;} |
- 매개 변수가 하나인 경우 자료형과 괄호 생략가능
str -> (System.out.println(str);} |
- 매개변수가 두 개 이상인 경우 괄호를 생략할 수 없음
x, y -> {System.out.println(x+y);} // 오류 |
- 실행문이 한 문장인 경우 중괄호 생략 가능
str -> System.out.println(str); |
- 실행문이 한 문장이라도 return(반환문)은 중괄호 생략할 수 없음
str -> return str.length(); //오류 |
- 실행문이 한 문장의 반환문인 경우엔 return과 중괄호를 모두 생략
(x, y) -> x+y; str -> str.length; |
함수형 인터페이스 선언하기
- 람다식을 선언하기 위한 인터페이스
- 익명 함수와 매개 변수만으로 구현되므로 인터페이스는 단 하나의 메서드만을 선언해야함
- @functionalInterface 애노테이션
함수형 인터페이스 라는 의미, 내부에 여러 개 의 메서드를 선언하면 에러남
@Funcional Interface
public interface Mynumber; { int getMax(int num, int num2); int add(int x, int y); } |
객체 지향 프로그래밍 vs 람다식 구현
인터페이스 구현
package lambda;
public interface StringConcat { public void makeString(String s1, String s2); } |
<클래스에서 인터페이스 구현>
StringConcatImpl 클래스에서 StringConcat 인터페이스를 구현
(StringConcat 인터페이스는 추상메서드 makString()을 가지고 있으므로 재정의)
package lambda;
public class StringConCatImpl implements StringConcat { @Override //추상메소드 makeString을 오버라이드(재정의) public void makeString(String s1, String s2) { System.out.println(s1 + "," +s2); } } |
메서드 테스트하기
package lambda;
public class TestStringConcat {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "World";
StringConCatImpl concat1 = new StringConCatImpl(); // 인스턴스 생성
concat1.makeString(s1, s2); // 참조변수 concat1을 사용해 makeString() 메서드 호출
}
}
<람다식으로 인터페이스 구현>
클래스를 따로 생성할 필요없이 바로 메서드를 구현
package lambda;
public class TestStringConcat { public static void main(String[] args) { String s1 = "Hello";
String s2 = "World"; StringConcat concat2 = (s, v) -> System.out.println(s + "," + v); // 두 매개변수 s, v를 사용해 연결된 문자열을 출력 concat2.makeString(s1, s2); } |
14-3. 스트림 (Stream)
스트림 이란?
- 자료의 대상과 관계없이 동일한 연산을 수행
배열, 컬렉션을 대상으로 연산을 수행 함
일관성 있는 연산으로 자료의 처리를 쉽고 간단하게 함
자료 처리에 대한 추상화가 구현되었다고 함
- 한번 생성하고 사용한 스트림은 재사용 할 수 없음
자료에 대한 스트림을 생성하여 연산을 수행하면 스트림은 소모됨
다른 연산을 수행하기 위해서는 스트림을 다시 생성해야 함
- 스트림 연산은 기존 자료를 변경하지 않음
자료에 대한 스트림을 생성하면 스트림이 사용하는 메모리공간은 별도로 생성되므로 연산이 수행되도 기존 자료에 한
변경은 발생하지 않음
- 스트림 연산은 중간 연산과 최종 연산으로 구분 됨
스트림에 대해 중간 연산은 여러 개의 연산이 적용될 수 있지만 최종 연산은 마지막에 한 번만 적용됨
최종연산이 호출되어야 중간 연산에 대한 수행이 이루어 지고 그 결과가 만들어짐
따라서 중간 연산에 대한 결과를 인산 중에 알 수 없음
이를 '지연 연산' 이라 함
중간 연산 (자료를 거르거나 변경하여 또 다른 자료를 내부적으로 생성)
- filter() : 조건을 넣고 그 조건에 맞는 참인 경우만 추출
스트림 생성 중간 연산 최종 연산
- map() : 클래스가 가진 자료 중 이름만 출력
스트림 생성 중간 연산 최종 연산
최종 연산 (생성된 내부 자료를 소모해 가면서 연산을 수행)
- forEach() : 요소를 하나씩 꺼냄
- count()
- sum()
- sum()
- reduce()
reduce() 연산
- 정의된 연산이 아닌 프로그래머가 직접 구현한 연산을 적용
T reduce(T identify, BinaryOperator<T> accumulator) |
- 최종 연산으로 스트림의 요소를 소모하며 연산을 수행
- 배열의 모든 요소의 합을 구하는 reduce() 연산 구현 예
Arrays.stream(arr).reduce(0, (a,b) -> a+b)); |
- reduce() 메서드의 두 번째 요소로 전달되는 람다식에 따라 다양한 기능을 수행 할 수 있음
- 람다식을 직접 구현하거나 람다식이 긴 경우 BinaryOperator를 구현한 클래스를 사용 함
예제 시나리오
1. 고객의 명단을 출력합니다.
2. 여행의 총 비용을 계산합니다.
3. 고객 중 20세 이상인 사람의 이름을 정렬하여 출력합니다.
TravelCustomer.java
package stream;
public class TravelCustomer { private String name; //고객 이름 private int age; //나이 private int price; //가격 public TravelCustomer(String name, int age, int price) { this.name = name; this.age = age; this.price = price; } public String getName() { return name; } public int getAge() { return age; } public int getPrice() { return price; } public String toString() { return "name: " + name + "age : " + age + "price : " + price; } } |
TravleTest.java
package stream;
import java.util.List; import java.util.ArrayList; public class TravleTest { public static void main(String[] args) { // 고객 생성 TravelCustomer customerLee = new TravelCustomer("이순신", 40, 100); TravelCustomer customerKim = new TravelCustomer("김유신", 20, 100); TravelCustomer customerHong = new TravelCustomer("홍길동", 13, 50); List<TravelCustomer> customerList = new ArrayList<>(); // ArrayList에 고객 추가 customerList.add(customerLee); customerList.add(customerKim); customerList.add(customerHong); System.out.println("=== 고객 명단 추가된 순서대로 출력 ==="); customerList.stream().map(c -> c.getName()).forEach(s -> System.out.println(s)); int total = customerList.stream().mapToInt(c -> c.getPrice()).sum(); System.out.println("총 여행 비용은 :" + total + "입니다"); System.out.println("=== 20세 이상 고객 명단 정렬하여 출력 ==="); customerList.stream().filter(c -> c.getAge() >= 20).map(c -> c.getName()).sorted().forEach(s -> System.out.println(s)); } } |
출력
=== 고객 명단 추가된 순서대로 출력 ===
이순신 김유신 홍길동 총 여행 비용은 :250입니다 === 20세 이상 고객 명단 정렬하여 출력 === 김유신 이순신 |
'Programming Language > JAVA' 카테고리의 다른 글
[Do it 자바 프로그래밍 입문] 16. 입출력 스트림 (0) | 2023.06.16 |
---|---|
[Do it 자바 프로그래밍 입문] 15. 예외 처리 (0) | 2023.06.15 |
[Do it 자바 프로그래밍 입문] 13. 컬렉션 프레임워크 (0) | 2023.06.14 |
[Do it 자바 프로그래밍 입문] 12. 자료구조 (1) | 2023.06.13 |
[Do it 자바 프로그래밍 입문] 11.기본 클래스 (0) | 2023.06.13 |