🍃프로그래머스 백엔드 데브코스 4기 교육과정을 듣고 정리한 글입니다.🍃
Interface
- 구현을 강제합니다.
- 클래스가 인터페이스를 구현한다면, 해당 클래스는 인터페이스에 정의된 모든 메서드를 구현해야 합니다.
- 다형성을 제공합니다.
- 인터페이스를 구현한 다양한 클래스들은 동일한 인터페이스 타입으로 다룰 수 있습니다.
- 의존성을 역전시킵니다.
- 클래스가 인터페이스만을 참조하여 작업을 수행하면, 의존성이 역전될 수 있습니다.
Interface 개선
default 메소드
// 인터페이스
interface Animal {
void makeSound();
// 디폴트 메소드
default void eat() {
System.out.println("Animal is eating");
}
}
// 어댑터 클래스
class AnimalAdapter implements Animal {
@Override
public void makeSound() {
// 구현하지 않음
}
}
// 구체적인 구현 클래스
class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("Dog is barking");
}
// eat() 메소드는 이미 Animal 인터페이스의 디폴트 구현을 가지므로 오버라이딩하지 않아도 됨
}
public class Main {
public static void main(String[] args) {
Animal dog = new Dog();
dog.makeSound(); // 출력: "Dog is barking"
dog.eat(); // 출력: "Animal is eating"
AnimalAdapter animalAdapter = new AnimalAdapter();
animalAdapter.makeSound(); // 출력: 없음
animalAdapter.eat(); // 출력: "Animal is eating"
}
}
- 자바 8부터 인터페이스 내에 직접 구현을 제공할 수 있습니다.
- 구현체는 기본 구현을 사용하거나 오버라이드 하여 사용할 수 있습니다.
- 필요한 메서드만 오버라이드 해서 사용하고 싶을 때 사용하는 Adapter 패턴의 역할을 해줍니다.
- 기존의 구현체의 수정 없이 인터페이스 기능 확장이 가능합니다.
static 메소드
// 인터페이스
interface MathOperation {
static int add(int a, int b) {
return a + b;
}
static int subtract(int a, int b) {
return a - b;
}
}
public class Main {
public static void main(String[] args) {
int num1 = 10;
int num2 = 5;
int sum = MathOperation.add(num1, num2);
int difference = MathOperation.subtract(num1, num2);
System.out.println("Sum: " + sum); // 출력: Sum: 15
System.out.println("Difference: " + difference); // 출력: Difference: 5
}
}
- 자바 8부터 인터페이스 내에 정적 메소드를 가질 수 있습니다.
- 인터페이스 이름을 통해 정적 메소드를 직접 출력할 수 있는 함수 제공자 역할을 수행할 수 있게 됩니다.
Functional Interface
@FunctionalInterface
interface MathOperation {
int operate(int a, int b);
}
public class Main {
public static void main(String[] args) {
MathOperation addition = (a, b) -> a + b;
System.out.println("Addition: " + addition.operate(5, 3)); // 출력: Addition: 8
MathOperation subtraction = (a, b) -> a - b;
System.out.println("Subtraction: " + subtraction.operate(5, 3)); // 출력: Subtraction: 2
}
}
- 함수형 인터페이스는 반드시 추상 메소드(함수)를 하나만 가져야 합니다.
- default, static 메소드는 여러 개 있어도 상관없습니다.
- 인터페이스에 @FunctionalInterface 어노테이션을 설정합니다.
java.util.funtion
Java에서 기본으로 제공하는 functional Interface
Predicate<T>
Predicate<String> isLengthGreaterThan5 = str -> str.length() > 5;
boolean result = isLengthGreaterThan5.test("Hello, World!");
System.out.println(result); // 출력: true
- 주어진 조건에 대해 참 또는 거짓을 반환하는 함수입니다.
- 예시: 문자열의 길이가 5보다 큰지 확인하는 Predicate
Function<T, R>
Function<Integer, String> convertToString = num -> String.valueOf(num);
String strNum = convertToString.apply(42);
System.out.println(strNum); // 출력: "42"
- 입력을 받아 변환한 결과를 반환하는 함수입니다.
- 예시: 정수를 문자열로 변환하는 Function
Consumer<T>
Consumer<String> printMessage = message -> System.out.println(message);
printMessage.accept("Hello, World!"); // 출력: "Hello, World!"
- 입력을 받아서 소비하는 함수로, 리턴값이 없습니다.
- 예시: 문자열을 출력하는 Consumer
Supplier<T>
Supplier<LocalDate> getCurrentDate = () -> LocalDate.now();
LocalDate currentDate = getCurrentDate.get();
System.out.println(currentDate); // 현재 날짜를 출력
- 제공하는 함수로, 매개변수가 없고 리턴값만 있는 형태입니다.
- 예시: 현재 날짜를 제공하는 Supplier
BiFunction<T, U, R>
BiFunction<Integer, Integer, String> multiplyAndConvertToString = (num1, num2) -> String.valueOf(num1 * num2);
String result = multiplyAndConvertToString.apply(5, 3);
System.out.println(result); // 출력: "15"
- 두 개의 입력을 받아서 변환한 결과를 반환하는 함수입니다.
- 예시: 정수 두 개를 곱한 결과를 문자열로 변환하는 BiFunction
UnaryOperator<T>
UnaryOperator<Integer> square = num -> num * num;
int result = square.apply(5);
System.out.println(result); // 출력: 25
- 하나의 입력을 받아서 동일한 타입의 결과를 반환하는 함수입니다.
- 예시: 정수를 제곱하는 UnaryOperator
인터페이스 생성
익명 클래스
public class Main {
public static void main(String[] args) {
Function<Integer, Integer> square = new Function<Integer, Integer>() {
@Override
public Integer apply(Integer x) {
return x * x;
}
};
int result = square.apply(5);
System.out.println(result); // 출력: 25
}
}
- 익명클래스를 사용해서 인터페이스의 인스턴스를 생성하고 구현을 바로 정의한다.
Lambda 표현식
public class Main {
public static void main(String[] args) {
Function<Integer, Integer> square = x -> x * x;
int result = square.apply(5);
System.out.println(result); // 출력: 25
}
}
- 익명 메소드를 사용해서 간결한 인터페이스 인스턴스 생성 방법입니다.
- 추상 메소드가 하나인, Functional Interface에서 가능합니다.
- 즉, 추상 메소드가 여러 개 있으면 익명 클래스를 사용해서 인터페이스 인스턴스를 생성해야 합니다.
- 매개변수의 타입을 생략할 수 있습니다.
- 단일 실행문인 경우 중괄호 '{}'와 'return'을 생략할 수 있습니다.
- 매개변수가 단 하나인 경우에는 괄호 '()'를 생략할 수 있습니다.
메소드 레퍼런스
public class Main {
public static int square(int x) {
return x * x;
}
public static void main(String[] args) {
Function<Integer, Integer> square = Main::square;
int result = square.apply(5);
System.out.println(result); // 출력: 25
}
}
- 람다 표현식에서 입력되는 값을 변경 없이 바로 사용하는 경우 클래스 타입::함수 표현 가능합니다.
- 입력값을 변경하지 말라는 표현방식이기도 합니다.
- 생성자 호출 표현은 클래스 타입::new 입니다.