정의기능의존성 역전(Dependency Inversion)부가 기능디폴트 메서드람다식람다식 사용 시, 주의점메소드 레퍼런스제너릭익명 클래스private 메서드인터페이스 use-case다중 인터페이스 구현Static fields and methods
정의
- 모든 메서드가 추상메서드로 선언됨 public abstract(아무것도 붙이지 않아도) → Class에서 implements시 public 안붙여주면 에러가 생김
- 모든 변수가 상수로 선언됨 public static final
- 구현 코드가 없으므로, 상속이 아닌 Calculator가 Calc를 구현한다고(Class Diagram에서 점선) 함

- CompleteCalc가 Calc를 타입상속했다 라고 말함. 구현상속과는 다름(구현 코드를 받은것)
Calc calc = new CompleteCalc();
가능함- 구현상속은 여러 클래스를 다중상속할수없음(모호성 존재) ← diamond problem
- 타입상속은 여러 클래스를 implements를 할 수 있음(모호성이 없음. 어차피 구현된것이 없기에)

기능
- 일종의 클라이언트 코드와의 약속이며, 클래스나 프로그램이 제공하는 명세
- 어떤 객체가 하나의 인터페이스 타입이라는 것은 그 인터페이스가 제공하는 모든 메서드를 구현했다는 의미 = 구현을 강제함
- 다형성을 제공함
의존성 역전(Dependency Inversion)
- 결합도를 낮추는 효과 (의존성을 역전)
public class Main{ public static void Main(String[] args){ new Main().run(LoginType.Naver); // LoginType - Enum } void run(LoginType loginType){ Login user = getLogin(loginType); user.login(); } // 이게 factory 패턴임 private Login getLogin(LoginType type){ if (type == LoginType.Naver) return new NaverLogin(); return new KakaoLogin(); } }
public class UserService implements Login { private Login login; // 의존성을 외부에 맡긴다면, 의존도율 낮춤 // KakaoLogin, NaverLogin 등의 구상체를 선언하게 되면 // 결합성이 높아지는 것임 추상체와 결합을 함 => 결합도가 낮아짐 // 의존성을 외부로부터 전달 받았다 => 의존성을 주입받았다 // 의존성 주임, Dependency Injection, DI // Dependency Inversion 됨 (어딘가에 의존하게 될 때, 구상체에 의존하지 말고 // 추상체에 의존해라. 의존성을 역전해서 의존하는 것임) public UserService(Login login){ this.login = login; } @Override public void login() { login.login(); } }

- 이 모듈이 무슨 일을 하는 지에 대한 명세. 쓸 때 그게 어떻게 돌아가는지 알 필요가 없으니 이때 interface가 매우 큰 역할을 함. 호출하는 쪽에서는 쓰는 방식이 똑같음
부가 기능
디폴트 메서드
- 인터페이스가 추상 메서드가 아닌 일반 메서드를 가질 수 있도록 해줌
- Adapter의 역할을 하게 됨(Adapter는 interface상에 있는 모든 메서드를 구현할 필요 없게 만들기 위하여 실제 이용하는 클래스와 interface 사이에 하나 더 추가하는 것) ⇒ default 메서드를 통해 Adapter가 필요 없게 됨
public interface MyInterface{ void method1(); void method2(); default int method3() { return 42; } } public MyInterfaceAdapter implements MyInterface{ @Override void method1(); @Override void method2(); } public MyService implements MyInterface{ } // 이 상황에서 MyService는 method1만 쓰고 싶은데 // 모든 메서드를 구현해야 하는 상황에 놓이게 됨 // 이 때, MyInterface에 default를 다 붙임으로써 // 사용하려는 메서드만 override해서 사용이 가능함
- 인터페이스 추가 만으로 기능을 확장할 수 있음(default 메서드로 기능이 구현되어 있기 때문에)
람다식
- 메서드도 익명으로 만들 수 있지 않을까 하는 생각에서 출발
- 내부적으로는 익명 클래스가 만들어지는 구조임
MyRunnable r1 = new MyRunnable(){ @Override public void run(){ System.out.println("Hello"); }; } MyRunnable r2 = () -> System.out.println("Hello"); // 람다표현식(익명메서드) // FunctionalInterface일때만 이런식으로 선언이 가능함. // 구현부가 한줄 밖에없으면 {} 이것도 생략 가능함 // 파라미터가 한개면 () 도 생략 가능함 // 다 FunctionalInterface임 MySupplier s = () -> "Hello World"; MyMapper m = (str) -> str.length(); // Method reference로 변경 가능함 // MyMapper m = String::length; MyConsumer c = i -> System.out.println(i); // System.out::println; MyRunnable r = () -> c.consume(m.map(s.supply())); // FunctionalInterface 사용하는 방법 void loop(int n, MyConsumer<Integer> consumer){ for(int i=0; i< n; i++){ consumer.consume(i); } } loop(10, System.out::println);
람다식 사용 시, 주의점
int strike = 0; int ball = 0; answer.indexedForEach((num, idx) -> { numbers.indexedForEach((num2, idx2) -> { if(!num.equals(num2)) return; if(idx.equals(idx2)) strike++; else ball++; }); });
- FunctionalInterface 안에서 바깥의 변수를 읽어올 수는 있지만 그 변수를 수정할 수는 없음. → ThreadSafe 하지 않음
- MultiThread에서 IndexedForEach를 통해서 값의 변경이 있게 되면 RaceCondition이 일어나게 됨
- 동기화 기능을 추가해 주어야함(AtomicInteger 사용)
public BallCount ballCount(Numbers answer, Numbers numbers){ AtomicInteger strike = new AtomicInteger(); AtomicInteger ball = new AtomicInteger(); answer.indexedForEach((num, idx) -> { numbers.indexedForEach((num2, idx2) -> { if(!num.equals(num2)) return; if(idx.equals(idx2)) strike.getAndIncrement(); else ball.getAndIncrement(); }); }); }
메소드 레퍼런스
- 람다 표현식에서 입력되는 값을 변경 없이 바로 사용하는 경우 (개발자의 개입을 차단함으로써 안정성을 얻을 수 있음. 변경하지 말고 그대로 사용하라는 의지의 표현!!)
- 최종으로 적용될 메소드의 레퍼런스를 지정해주는 표현 방식
제너릭
- 위의 경우에서 반환 되는 output, input의 형태를 바꾸기 위해서는?? Generic 사용!!
public interface MySupplier<T> { T supply(); } public interface MyMapper<IN, OUT>{ OUT map(IN); } MySupplier<String> s = () -> "Hello World"; MyMapper<String, Integer> m = String::length;
익명 클래스
- 인터페이스 이용하려면 항상 구현을 해야 하기에 클래스를 만들어야 한다. ⇒ 불편해서 한번에 하는 것이 익명 클래스
new Runnable(){ @Override public void run(){ //~~~~ } }.run();
private 메서드
- 인터페이스 안에 default 메서드가 여러 개 있을 때, private 메서드를 만들어서 재사용 가능함
interface SomeInterface { void method1(); String method2(int i); default int method3() { return getNumber(); } default int method4() { return getNumber() + 22; } private int getNumber() { return 42; } }
인터페이스 use-case
하나의 클래스가 여러 인터페이스를 구현할 수 있음
public class A implements B, C {} ;
인터페이스 사이의 상속
public interface MyInterface extends X, Y{};
- extends 뒤에 여러 개가 나오면 얘네들 다 인터페이스 인것.
클래스 상속과 인터페이스 같이 사용(extends 먼저, implements후에)
public class BookShelf extends Shelf implements Queue{};
다중 인터페이스 구현
- 디폴트 메서드가 중복 되는 경우는 구현하는 클래스에서 재정의 하여야 함
public interface Buy { void buy(); default void order() { System.out.println("buy order"); } } public interface Sell { void sell(); default void order() { System.out.println("sell order"); } } public class Customer implements Buy, Sell{ @Override public void sell() { System.out.println("customer sell"); } @Override public void buy() { System.out.println("customer buy"); } @Override public void order() { System.out.println("customer order"); } public void hello() { System.out.println("hello"); } }
Static fields and methods
- Java8 이후부터 인터페이스 안에 선언된 모든 필드는 public, static, final 임 → interface가 constant를 두기에 적합한 장소가 됨
- 자바 8 이전에는 인터페이스에 정적 메서드 선언이 불가능했음(그래서 Companion class 같은 것이 만들어졌었음)
- 자바 8 부터 인터페이스가 정적 메서드 가질 수 있음. 자바 8에서는 public 정적 멤버만 허용하지만, 자바 9 부터는 private 정적 메서드까지는 허락함(private 메서드도 가능함). 그러나 정적 필드와 정적 멤버 클래스는 여전히 public이어야 함!