인터페이스 : 모든 메소드가 추상 메소드로 구성된 클래스
따라 구현을 강제한다. 메소드를 구현하지 않았을 때 오류가 발생하고, (인텔리제이 기준) Alt
+ Enter
키로 빠른 수정을 하면 구현하지 않은 메소드가 자동으로 구현하게끔 생겨난다.
interface MyRunnable {
void myRun();
}
interface YourRunnable {
void yourRun();
}
public class Test implements MyRunnable, YourRunnable {
public static void main(String[] args) {
Test t = new Test();
t.myRun();
t.yourRun();
MyRunnable mr = new Test();
mr.myRun();
YourRunnable yr = new Test();
yr.yourRun();
}
@Override
public void myRun() {
System.out.println("My Run");
}
@Override
public void yourRun() {
System.out.println("Your Run");
}
}
위 코드처럼 Test
객체를 생성했을 땐 두 인터페이스의 메소드를 호출 가능하지만, 특정 인터페이스의 객체를 생성했을 땐 인터페이스에 정의된 메소드만 호출 가능하다.
따라 특정 메소드를 호출하고 싶을 때, 각 클래스 객체를 여러 개 생성하지 않고 인터페이스 객체를 생성하여 프로그램을 실행할 때 결정지어 호출할 수 있도록 돕는 역할을 한다. (어떤 것이든 받아들일 수 있음)
public class UserService implements Login {
private Login login;
//private Login login = new KakaoLogin();
//위 코드라면 경우 오직 카카오로그인밖에 할 수 없게 된다.
public UserService(Login login) {
this.login = login;
}
@Override
public void login() {
login.login();
}
}
private
객체 login
이 없다면 login()
메소드 역시 실행할 수 없기 때문에 메소드는 login
에 의존하는 상태이다. 의존 객체를 주석 처리한 코드처럼 작성하게 되면 코드 작성자에 의해 결정된다. 하지만 생성자를 통해 의존성을 외부에 맡긴다면 여러 기능을 수행할 수 있게 된다. (의존도를 낮춘다.)
추상체와 결합하게 되면 결합도가 낮아진다. (구상체와 결합 : 주석 처리한 코드처럼 여러 구상체 객체 생성할 시.)
의존성을 외부로부터 전달 받음 → 의존성 주입 받음, Dependency Injection (DI)
클래스 → 여러 구상체 의존하게 되면 한 방향으로 의존하게 되지만,
클래스 → 추상체를 의존한다면, 추상체 ← 여러 구상체의 상속 구조를 가지게 된다. Dependency Inversion
자바의 기능 개선 → 인터페이스도 구상 메소드를 가질 수 있게 됨
public interface Login {
void login();
default void sayLogin(){
System.out.println("Login");
}
}
오버라이드 하지 않아도 호출 가능.
사용하는 이유
default
통하여 전부 오버라이드하지 않아도 됨.static
메소드를 가질 수 있게 됨정의 : 추상 메소드가 하나밖에 없는 메소드. (@FuntionalInterface
) default
, static
등의 메소드는 상관 없다.
익명 클래스를 사용하여 인터페이스 인스턴스를 생성하고(new), 구현을 바로 정의한다.
new Login() {
@Override
public String supply() {
return "Hello";
}
}.supply();
이렇게 바로 호출 가능하다.
Login login = new Login() {
@Override
public String supply() {
return "Hello";
}
};
바로 인스턴스를 생성하고 구현부를 작성한다.
//MyRunnable이 함수형 인터페이스일 때.
MyRunnable r1 = new MyRunnable() {
@Override
public void myRun() {
System.out.println("Hello");
}
};
MyRunnable r2 = () -> System.out.println("Hello");
//return이 있을 때는 return 역시 생략
위 두 식은 같은 기능을 한다. (당연한 것을 생략)
객체를 MyRunnable
형으로 생성 → new MyRunnable
생략 가능
함수형 인터페이스 → 오버라이드 메소드 생략 가능
MyRunnable r2 = () { }
로 표현되는데, 이를 구분하기 위해 → 사용.
구현부가 한 줄뿐이므로 { }
생략하여 위와 같은 식 생성 가능.
람다 표현식의 정의 : 익명 메소드를 사용해서 간결한 인터페이스의 인스턴스 생성 방법. 오직 함수형 인터페이스에서만 가능하다.
인텔리제이에서 [ 빠른 수정 ] 기능을 통해 바로 람다 변환 가능하다.
람다 표현식에서 입력되는 값을 변경 없이 바로 사용하는 경우, 최종으로 적용될 메소드 레퍼런스를 지정해주는 표현 방식.
MyMapper m = (str) -> str.length();
MyConsumer c = i -> System.out.println(i);
//람다 with method reference
MyMapper m = String::length;
MyConsumer c = System.out::println;
m : 매개변수로 str
을 받고 String
클래스의 length()
를 호출한다. 이때 매개변수를 바로 활용하므로 생략하여 표현할 수 있다.
c : 매개변수 i
를 그대로 출력하기 때문에 생략한다.
단, 매개변수만 사용하는 경우가 아닐 경우 사용할 수 없다.
전부 인텔리제이 [ 빠른 수정 ] 기능으로 표현할 수 있다.
위와 같은 람다 표현식에서, 리턴 타입을 변경하고 싶을 때 하나하나 변경할 필요 없이 인터페이스에서 제너릭 타입을 지정하면 된다.
public interface MyMapper<IN, OUT> {
OUT map (IN s);
}
MyMapper<String, Integer> m1 = String::length;
MyMapper<Integer, Integer> m2 = i -> i * i;
//MyMapper<Integer, String> m2 = i -> Integer.toHexString(i);
MyMapper<Integer, String> m2 = Integer::toHexString;