예제 서비스 : 정수 시퀀스에서 처음 n개 앖의 평균을 보고하는 서비스
public static double average(IntSequence seq, int n)
예제 인터페이스
public interface IntSequence {
boolean hasNext();
int next();
}
// 인터페이스의 모든 메서드는 자동으로 public이 된다.
interface를 이용한 average 메서드 구현
public static double average(IntSequence seq, int n) {
int count = 0;
doulble sum = 0;
while (seq.hasNext() && count < n) {
count++;
sum += seq.next();
}
return count == 0 ? 0 : sum / conut;
}
예제 : 제곱수 수열
public class SquareSequence implements IntSequence {
private int i;
@Override
public boolean hasNest() {
return true;
}
@Override
public int next() {
i++;
return i * i;
}
}
다음 코드로 1부터 100 까지 자연수들의 제곱수의 평균을 구할 수 있다.
IntSequence squares = new SquareSequence();
double avg = average(squares, 100);
IntSequence squares = new SquareSequence();
double avg = average(squares, 100);
위 코드의 IntSequence 타입 변수는 SquareSequence 클래스의 인스턴스를 참조하고 있다.
IntSequence 타입 변수는 IntSequence 인터페이스를 구현한 어떤 클래스의 객체라도 참조할 수 있다.
IntSequence는 SquareSequence의 슈퍼타입이다.
슈퍼타입 변수를 서브타입으로 변환할 때는 타입 변환(cast)이 필요하다.
IntSequence sequence = new SquareSequence();
SquareSequence squares = (SquareSequence) sequence;
잘못된 캐스팅을 할 경우 컴파일 오류 혹은 classCastException 이 발생한다.
instanceof 연산자로 실수를 방지할 수 있다.
if (sequence instanceof SquareSequence) {
...
}
위 코드의 if문 내 표현식은 SquareSequence 클래스가 sequence 변수의 타입의 슈퍼타입일 경우 true를 반환한다.
public interface Closeable {
void close();
}
위 Closeable 이라는 인터페이스를 아래 Channel 인터페이스 처럼 확장할 수 있다.
public interface Channel extends Closeable {
boolean isOpen();
}
Channel 인터페이스를 구현하는 클래스는 반드시 Closeable에 있는 메서드들과 Channel에 있는 메서드들 모두를 구현해야 한다.
클래스는 인터페이스를 몇 개든 구현할 수 있다.
public class FileSequence implements IntSequence, Closeable {
...
}
위 경우 FileSequence 클래스는 IntSequece와 Closeable을 슈퍼타입으로 둔다.
인터페이스에 정의된 변수는 자동으로 public static final 이다.
따라서 (interface name).(constant name) 으로 접근 가능하다.
하지만 상수 집합에는 enumeration을 사용하는 것이 훨씬 바람직하다.
일반적으로 인터페이스의 메서드들은 구현이 없는 추상 메서드이나, 구현을 포함한 정적 메서드도 선언이 가능하다.
public interface IntSequence {
...
public static getSquareSequenceInstance() {
return new SquareSequence();
}
}
IntSequence squares = IntSequence.getSquareSequenceInstance();
public interface IntSequence {
default boolean hasNext() {
return true;
}
int next();
}
위 코드의 hasNext() 메서드는 기본 메서드이며, 이 인터페이스를 구현하는 클래스는 hasNext 메서드를 오버라이드 하거나 기본 구현을 상속하는 방법 중 하나를 선택할 수 있다.
Person interface와 Identified interface 두 인터페이스 모두 getId() 메서드를 default로 구현하여 가지고 있다고 하자.
그럼 아래와 같은 코드는 컴파일 에러를 발생시킨다.
public class Employee implements Person, Identified {
...
}
이러한 에러를 해결하기 위한 방법은
1. Employee 클래스가 getId() 메서드를 오버라이드해서 본인만의 구현을 가지면 된다.
2. 아래 코드처럼 슈퍼타입들의 default 구현중 하나를 선택하도록 한다.
public class Employee implements Person, Identified {
public int getId() {
return Identified.super.getId(); // super 키워드로 슈퍼타입 메서드를 호출 가능
}
}
적어도 한 인터페이스에서 구현을 제공하면 컴파일러는 오류를 보고한다.
Comparable interface (java.lang.Comparable)
public interface Comparable<T> {
int compareTo(T other);
}
x.compareTo(y) 는 정수를 반환한다. 이 정수는 x와 y의 순서를 결정한다.
리턴값이 양수이면 x 다음 y가 온다.
음수이면 y 다음 x가 온다.
0이면 x와 y가 같다.
public class Employee implements Comparable<Employee> {
public int compareTo(Employee other) {
return Double.compare(salary, other.salary);
}
}
// ? compare 메서드가 other.salary에 접근하는 것은 합법적이다. 자바의 메서드는 자신이 속한 클래스의 private 객체에 접근할 수 있다.
Comparator interface (java.util.Comparator)
public interface Comparator<T> {
int compare(T first, T second);
}
문자열을 문자열의 길이로 비교
class LengthComparator implements Comparator<String> {
public int compare(String first, String second) {
return first.length() - second.length();
}
}
test code
List<String> friends = new ArrayList<String>(Arrays.asList("Peter", "Paulson", "Marian"));
System.out.println("original : " + friends);
friends.sort(null);
System.out.println("dictionary order : " + friends);
friends.sort(new LengthComparator());
System.out.println("order by length : " + friends);
result
original : [Peter, Paulson, Marian]
dictionary order : [Marian, Paulson, Peter]
order by length : [Peter, Marian, Paulson]
한 번 이상 나중에 실행할 수 있게 전달하는 코드 블록.
유용한 상황
자바는 함수 타입이 없으며, 함수를 객체로 표현.
특정 인터페이스를 구현하는 클래스의 인스턴스로 표현.
람다 표현식은 이런 인스턴스를 생성하는 아주 편리한 문법을 제공.
예제
(String first, String second) -> first.length() - second.length()
표현식이 여러줄일 경우
(String first, String second) -> {
int difference = first.length() - second.length();
if (difference < 0) {
return -1;
} else if (difference > 0) {
return 1;
}
return 0;
}
파라미터가 없는 경우 빈 소괄호 사용
Runnable task = () -> {
for (int i=0; i < 1000; i++) {
doWork();
}
}
람다 표현식의 파라미터 타입을 추론할 수 있다면 파라미터 타입을 생략할 수 있다.
Comparator<String> comp = (first, second) -> first.length() - secound.length();
람다 표현식의 파라미터 타입을 추론할 수 있고, 파라미터가 한개라면 소괄호도 생략할 수 있다.
EventHandler<ActionEvent> listener = event -> System.out.println("Oh noes!");
람다 표현식의 결과 타입은 명시하지 않는다.
컴파일러는 구현부로부터 결과 타입을 추론해서 기대하는 타입과 일치하는지 검사한다.
추상 메서드가 한 개만 포함된 인터페이스를 함수형 인터페이스라고 한다.
함수형 인터페이스에 람다 표현식을 사용할 수 있다.
List.sort 메서드의 파라미터 변수는 Comparator 인터페이스를 구현하는 클래스의 객체를 받는다. 다음과 같이 파라미터로 람다 표현식을 전달할 수 있다.
List<String> friends = new ArrayList<String>(Arrays.asList("Peter", "Paulson", "Marian"));
friends.sort((first, second) -> first.length() - second.length());
위 표현식의 파라미터 변수는 Comparator 인터페이스를 구현하는 클래스의 객체이다. 이 객체의 compare 메서드를 호출하면 람다 표현식의 구현부를 실행한다.
다른 코드에 전달하려는 액션을 수행하는 메서드가 이미 있을 때 사용하는 메서드 참조용 특수 문법.
Arrays.sort(strings, (x, y) -> x.compareToIgnoreCase(y));
Arrays.sort(strings, String::compareToIgnoreCase);
세 가지 형태
1) 클래스::인스턴스메서드
첫 번째 파라미터가 메서드의 수신자, 나머지 파라미터는 해당 메서드로 전달된다.
String::compareToIgnoreCase
(x, y) -> x.compareToIgnoreCase(y)
2) 클래스::정적메서드
모든 파라미터가 정적 메서드로 전달된다.
Object::isNull
x -> Object.isNull(x)
3) 객체::인스턴스메서드
주어진 객체에서 메서드가 호출되며, 파라미터는 인스턴스 메서드로 전달된다.
System.out::println
x -> System.out.println(x)
메서드 이름이 new라는 점만 제외하면 메서드 참조와 같다.
List<String> names = ...;
names.stream().map(Employee::new);
Employee[] buttons = stream.toArray(Employee[]::new);
를 통해 Obejct[] 가 아닌 특정 클래스 배열을 얻을 수 있다.
지연 실행의 이유
public static void repeat(int n, Runnable action) {
for (int i=0; i < n; i++) {
action.run();
}
}
repeat(10, () -> System.out.println("Hello World"));
,
public interface IntConsumer {
void accept(int value);
}
public static void repeat(int n, IntConsumer action) {
for (int i=0; i < n; i++) {
action.accept();
}
}
repeat(10, i -> System.out.println("Countdown: " + (9 - i)));