JAVA 8(2) - 기본 메소드와 스태틱 메소드
인터페이스 기본 메소드와 스태틱 메소드
자바의 인터페이스 기능은 추상메소드와는 다릴 메소드 선언만 할 뿐 그 내부에 로직이 들어가는 일이 없었다. 하지만 java8부터 인터페이스에도 메소드 선언이 아니라 구현이 가능해졌는데 이때
default method
와static method
방법이 있다.
기본 메소드 (Default Method)
- 인터페이스에 메소드 선언이 아니라 구현체를 제공하는 방법
- 해당 인터페이스를 구현한 클래스를 깨뜨리지않고 새 기능의 추가가 가능하다.
- 기본 메소드는 구현체가 모르게 추가된 기능으로 리스크가 있다.
- 컴파일 에러는 아니지만 구현체에 따라 런타임 에러가 발생할 수 있다.
- 반드시 문서화가 필요하다 (@implSpec 태그 사용)
- Object 가 제공하는 기능(equals, hasCode)는 기본메소드를 제공할 수 없다.
- 인터페이스를 상속받는 인터페이스에서 다시 추상 메소드로 변경할 수 있다.
- 인터페이스 구현체에서 재정의가 가능하다.
기존에 이름을 출력하는 PringName
인터페이스가 있었고 많은 클래스에서 해당 인터페이스를 상속받고 있었다고 가정할 때,
PringName
에 대문자로 이름을 출력하는 새로운 가상메소드를 선언한다면 상속받은 많은 클래스에서 구현을 하지않았기 때문에 에러가 발생할 것이다.
public interface PrintName {
void pringName();
// void printNmaeUpperCase();
}
이 때 기본 메소드를 사용한다면 구현체에서 따로 메소드를 구현하지 않아도 사용이 가능해진다.
public interface PrintName {
void printName();
default void printNameUpperCase(){
System.out.println(getName().toUpperCase());
}
String getName();
}
하지만 위 코드처럼 기본 메소드인 printNameUpperCase()
는 구현체가 구현한 getName()
메소드를 호출해서 toUpperCase()
를 호출하지만, 구현체의 getName()
에서 null이 반환된다면 에러가 발생할지도 모른다.
그렇기 때문에 @implSpec 애노테이션을 사용해서 문서화를 해야한다,
/**
* @ImplSpec
* 이 구현체는 getName()으로 가져온 문자열을 대문자로 변환 후 출력한다.
*/
default void printNameUpperCase(){
System.out.println(getName().toUpperCase());
}
추가로 기본 메소드는 위의 클래스처럼 사용자가 직접 정의한 인터페이스에서만 사용이 가능하며 외부 라이브러리등에 추가해 사용하는 것은 불가능하다.
✔ 만약 인터페이스를 상속하는 인터페이스에서는 기본 메소드가 필요 없는 경우
/**
* @implSpec
* 이 구현체는 한국어 인삿말을 출력합니다.
*/
default void hello(){
System.out.println("안녕하세요");
public interface Bar extends PrintName{
void hello();
}
👉 해당 인터페이스는 이 메소드를 다시 추상 메소드로 변경하면 된다.
✔ 구현체가 두 개의 인터페이스를 상속받았을 때 동일한 기본메소드가 있는 경우
Error: DefaultFoo inherits unrelated defaults for hello() from types Foo and Bar
어느 기본메소드인지 모호하기 때문에 에러가 발생한다.
👉 이 경우 직접 오버라이딩을 해서 두 개의 인터페이스에서 모두 가지고 있는 메소드를 재정의 해줘야 한다.
스태틱 메소드 (Static Method)
- 해당 타입 관련 헬퍼 또는 유틸리티 메소드를 제공할 때 인터페이스에 스태틱 메소드를 제공할 수 있다.
public interface Foo {
static void helloAll(){
System.out.println("인삿말");
}
}
public class App {
public static void main(String[] args) {
//static 메소드 사용
Foo.helloAll();
}
}
# 출력결과
> 인삿말
자바 8 API의 기본 메소드와 스태틱 메소드
Collection
- foreEach
조금더 쉽게 순회를 가능하게 해주는 메소드
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("jinsoo");
names.add("jin");
names.add("soo");
names.add("bae");
names.forEach(System.out::println);
/* 기존의 for of 문을 사용한 방법
for (String name : names) {
System.out.println(name);
}*/
}
- spliterator
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("jinsoo");
names.add("jin");
names.add("soo");
names.add("bae");
Spliterator<String> spliterator = names.spliterator();
Spliterator<String> trySplit = spliterator.trySplit();
while(spliterator.tryAdvance(System.out::println));
System.out.println("=========================");
while(trySplit.tryAdvance(System.out::println));
}
# 출력 결과
soo
bae
=======
jinsoo
jin
-
Spliterator<String> spliterator = names.spliterator();
분할할 수 있는 기능
-
Spliterator<String> trySplit = spliterator.trySplit();
해당 spliterator에서 절반의 요소를 꺼내 새로운 splierator를 만들어 반환함
-
spliterator.tryAdvance(System.out::println)
내부 메소드에 파라미터로 Functional Interface인 Consumer 가 들어오며 더 이상 들어올게 없을 경우 false를 반환
이 예제에서는 들어온 값을 단순히 출력하는 메소드 레퍼런스로 사용함
- removeIf
Collection 요소를 순회하며 인자로 넘겨주는 함수를 Functional Interface인 Predicate에 넘겨서 true를 반환하는 값을 삭제함
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("jinsoo");
names.add("jin");
names.add("soo");
names.add("bae");
names.removeIf(s -> s.startsWith("s"));
names.forEach(System.out::println);
}
-
names.removeIf(s -> s.startsWith("s"));
names를 순회하며
s
로 시작하는 단어를 찾아 삭제함
Comparator
정렬에 사용되는 인터페이스
- reversed
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("jinsoo");
names.add("jin");
names.add("soo");
names.add("bae");
Comparator<String> compareToIgnoreCase = String::compareToIgnoreCase;
names.sort(compareToIgnoreCase.reversed());
names.forEach(System.out::println);
}
댓글남기기