이숭간 공부기록

[Java] 인터페이스 (Default Method, 함수형인터페이스, 람다 표현식) 본문

공부공부/Java

[Java] 인터페이스 (Default Method, 함수형인터페이스, 람다 표현식)

이숭간 2021. 8. 4. 21:44
728x90

인터페이스란?

  • 기본설계도
  • 추상메서드와 상수만 가지며, 데이터를 저장하지않는 개발코드와 객체의 통신접점을 의미한다.
  • interface키워드로 선언되며 인터페이스는 다중상속이 가능하다

인터페이스의 구현

  • 인터페이스의 모든 추상메소드를 구현한 구현클래스를 작성한다.
  • implements키워드를 사용해서 구현한다.
  • 만약 구현하는 인터페이스가 어떤 상위 인터페이스를 상속하고 있는경우 구현클래스는 상위인터페이스의 모든 추상메소드까지 전부 구현해야한다.
    //추상체
    public interface Login {
        void login();
    }
    //구현체
    public class KakaoLogin implements Login{
        @Override
        public void login(){
            System.out.println("카카오로 로그인 합니다.")
        }
    }

    public class NaverLogin implements Login{
        @Override
        public void login(){
            System.out.println("네이버로 로그인 합니다.")
        }
    }

    public class UserService implements Login{
        private Login login; 
        public UserService(Login login){
            this.login = login;
        }
        @Override
        public void login(){
            login.login();
        }
    }

    public class Main() {
        public static void main(String[] args){
            UserService s = new UserService(new KaKaoLogin());
            s.login();
        }
    }

인터페이스의 목적

  • 상속받을 서브클래스에게 구현할 메서드의 원형을 알려주고, 이를 구현하도록 하여 클래스의 동작방식을 일치시킨다.
  • 구현객체의 같은 동작을 보장하기위한 목적
  • 예를들어, 서로 관련없는 클래스에서 공통적으로 사용하는 방식이 필요하지만, 기능은 각각 구현해야할 필요가 있는경우 사용한다.
    • EX) Spring Data JPA에서 지원하는 crudRepository interface가 있습니다. 데이터베이스 사용시 crud의 동작방식은 같지만, 어떤 데이터베이스를 사용하는지에 따라 그 구현에는 차이가 있으므로 이럴때 인터페이스를 사용할 수 있다.

인터페이스의 장점

  • 메서드를 호출하는 쪽에서 메서드 내용에 관계없이 선언부만 알면 되기때문에 독립적인 프로그래밍이 가능하다.
  • 인터페이스는 선언부만 선언해두기 떄문에 틀만 만들어놓고 일관되고 정해진 규칙에 따른 프로그램개발이 가능하다.
  • 부모자식 관계가 아니더라도 인터페이스를 사용하면 각 클래스 사이에 관계를 맺어줄수 있다.

 

추상클래스와 인터페이스의 공통점

  • 인스턴스를 생성할 수 없다.
  • 자식클래스 또는 구현클래스가 추상 메서드의 구체적인 동작을 구현하도록 책임을 위임한다.

추상클래스와 인터페이스의 차이점

  • 애초에 목적이 다르다.
    • 추상클래스는 추상메서드를 자식이 구체화하여 그 기능을 확장하는데 목적이 있다.(is kind of) (ex) 가전제품 - TV, 냉장고)
    • 인터페이스는 구현 객체의 같은 동작을 보장하는데 목적이 있다.(can do this) ( ex) 날수 있다 - 비행기, 새)
  • 추상클래스는 클래스이지만 인터페이스는 클래스가 아니다.
  • 추상클래스는 단일상속이지만 인터페이스는 다중상속이 가능하다. ( 추상클래스는 일반메소드도 포함하기 때문)

Default Method

  • 자바8부터 기능이 강화됨
  • 인터페이스가 구현체를 가질수 있게됨
  • 따라서 디폴트 메소드를 사용하면 기본적인 기능을 탑재한 인터페이스를 만들 수 있다.
    • 디폴트 메소드를 갖는 인터페이스를 어떤 클래스가 구현하면, 그 클래스에서 인터페이스의 디폴트메소드를 오버라이드 하지 않아도 기능을 사용할수 있음
  • Adapter역할을 하게됨
    • 인터페이스에 추상메서드가 10개있다고 해보자
    • 어떤 A 클래스에서 그중 1개의 메서드만 필요해서 인터페이스를 구현할경우, 필요없는 나머지 9개의 메서드도 무조건 오버라이드 해줘야하는 상황이 발생한다. 내용없이..
    • 그래서 Adapter라는것을 두어 이 Adapter클래스에서 우선적으로 Interface의 10개 메서드를 모두 오버라이딩한다.
    • 그후 A클래스에서는 인터페이스를 implements하는것이 아니라 Adapter클래스를 상속받으면된다.
    • 그러나 이미 A클래스가 상속받고있는 클래스가 있다면?? 자바에서는 다중상속이 안되기 때문에 울며겨자먹기로 Interface를 구현할수밖에 없다.
    • 이럴때 default메서드를 쓰면 되는것이다!! 나머지 9개를 오버라이드 하지 않아도됨
  • 인터페이스 추가만으로 (구현없이!) 기능을 확장할 수 있게 됨
  • staic메서드도 갖게됨 → 객체생성없이 인터페이스의 static메서드 호출가능, 즉 기능자체만을 담는 기능도 수행하게됨 ( 함수제공자 )

Functional Interface (함수형 인터페이스)

  • 자바8에서부터 추가됨
  • 추상메소드가 오직 하나만 존재하는 인터페이스
    • default나 static메서드가 있어도 상관없다.
  • @FuntinalInterface를 달아준다.

인터페이스 임시생성하기

  • 익명클래스를 사용해서 인터페이스의 인스턴스를 생성하고 구현을 바로 정의한다.
public class Main {
	public static void main(String[] args){
		new MySupply(){ // 인터페이스를 바로 new하는 형식으로 익명클래스 생성하여 supply함수 호출
			@Override
			public String supply() {
					return "Hello World";
		}
	}.supply();

		new class XXX implements MySupply {
				@Override
				public String supply() {
					return "Hello World';
			}
		}.supply();
	}
}
  • 어떤 인터페이스를 구현하여 메소드를 오버라이드한 클래스의 객체를 만들어서 그 메소드를 호출 → 이름없는 클래스 ( 익명클래스)로 만듬

 

  • Runnable의 run메서드는 MySupply의 supply메소드를 이용하는 메서드
MyRunnable r= new MyRunnable(){
    @Override
    public void run() {
        MySupply s= new MySupply(){
            @Override
            public String supply(){
                return "Hello World";
            }
        };
        System.out.println(s.supply());
    }
};
r.run();

익명으로 클래스를 만들수있다면 메서드도 익명으로 사용할 수 없을까?

 

람다 표현식

  • 익명메소드를 사용해 간결한 인터페이스
  • Functionalinterface (추상메서드를 1개만 갖는 인터페이스) 에 한해서만 가능
    • 어차피 오버라이드할 메서드가 뻔하게 하나밖에 없잖아!
  • 간결한 표현이 가능
public class Main2{
    public static void main(String[] args){
        
        // 이랬던 코드가 
        new MyRunnable() {
            @Override
            public void run() {
                System.out.println("Hello");
            }
        }.run();

        // 이렇게 한줄로 변신 
        MyRunnable r = () -> System.out.println("Hello");

        // 예시 -> 바로 윗 블록 13줄 코드와 같음
        MyRunnable r3 = () -> {
            MySupply s2 = () -> "Hello Hello";
            System.out.println(s2.supply());
        };

        r3.run();
        
    }
}

 

메소드 레퍼런스

  • 람다표현식에서 입력되는 값을 변경없이 바로 사용하는 경우
  • 최종으로 적용될 메소드의 레퍼런스를 지정해 주는 표현방식
  • 장점 : 입력된 값의 변경이없음을 보장할 수 있다 (
    • 들어온값 변경없이 그대로 사용해!!!! 즉 입력값을 변경하지 말라는 표현방식이기도 하다.
    • 아 이렇게 해놓은이유는 입력값을 변경하지 말라는거구나, 내가 함부로 바꾸면 안되겟다!
    • 개발자의 개입을 차단함으로써 안정성을 얻을 수 있다.