본문 바로가기

개인적으로 공부한 것을 정리해 놓은 블로그입니다 틀린 것이 있으면 댓글 부탁 드립니다!


JAVA

JAVA 스터디 14 - 인터페이스

반응형

인터페이스란?

 자식 클래스가 여러 부모 클래스를 상속받을 수 있다면 , 다양한 동작을 수행할 수 있다는 장점을 가지게 될 것이다.

하지만 클래스를 이용하여 다중 상속을 할 경우 메소드 출처의 모호성 등 여러 가지 문제가 발생할 수 있어 자바에서는 클래스를 통한 다중 상속을 지원하지 않는다. 

 하지만 다중 상속의 이점을 버릴 수는 없기에 자바에서는 인터페이스라는 것을 통해 상속을 지원하고 있다. 

인터페이스란 다른 클래스를 작성할 때 기본이 되는 틀을 제공하며 , 다른 클래스 사이의 중간 매개 역할까지 담당하는 일종의 추상 클래스를 의미한다. 

 자바에서 추상 클래스는 추상 메소드 뿐만 아니라 생성자 , 필드 , 일반 메소드도 포함할 수 있다 .

하지만 인터페이스는 오로지 추상 메소드와 상수만을 포함할 수 있다 .

 

인터페이스의 선언

 자바에서 인터페이스를 선언하는 방법은 클래스를 작성하는 방법과 같다 . 인터페이스를 선언할 때에는 접근 제어자와 함께 interface 키워드를 사용하면 된다 .

 자바에서 인터페이스는 다음과 같이 선언한다 .

 

 

접근제어자 inteface 인터페이스이름{
        public static final 타입 상수이름 = 값 ;

        public abstract  메소드이름(매개변수목록);
        
    }

 

단 , 클래스와는 달리 인터페이스의 모든 필드는 public static final 이어야 하며 , 모든 메소드는 public abstract이어야 한다 . 이 부분은 모든 인터페이스에 공통으로 적용되는 부분이므로 이 제어자는 생략할 수 있다 . 

 이렇게 생략된 제어자는 컴파일 시 자바 컴파일러가 자동으로 추가해 준다.

 

인터페이스 구현하는 방법

 인터페이스는 추상 클래스와 마찬가지로 자신이 직접 인스턴스를 생성할 수 는 없다 . 따라서 인터페이스가 포함하고 있는 추상 메소드를 구현해 줄 클래스를 작성해야만 한다.  자바에서 인터페이스는 다음과 같은 문법을 통해 구현한다 . 

 

자바에서 인터페이스는 아래와 같이 구현된다 

 

class 클래스이름 implements  인터페이스이름 {}

 

만약 모든 추상 메소드를 구현하지 않는다면 , abstract 키워드를 사용하여 추상 클래스로 선언해야 한다 .

 

interface Human{public abstract void sayMyName();}
interface Animals{public abstract void eatSomething();}

//인터페이스를 통해 다중 상속이 가능하다.
class Ugo implements Human,Animals{
    @Override
    public void sayMyName() {
        System.out.println("my name is Ugo");
    }

    @Override
    public void eatSomething() {
        
    }
}

class Hong implements Human{
    @Override
    public void sayMyName() {
        System.out.println("my name is Hong");
    }
}


public class InterfaceTest {
    public static void main(String[] args) {
        Hong h = new Hong();
        Ugo ugo = new Ugo();
        // 다형성을 통해 클래스를 인터페이스 타입으로 받을 수 있다 .
        Human ugo1 = new Ugo();
        h.sayMyName();
        ugo.sayMyName();
    }
}

 

* 인터페이스는 인터페이스로부터만 상속을 받을 수 있으며 ,여러 인터페이스를 상속 받을 수 있다 .

 

인터페이스의 장점

 인터페이스를 사용하면 다중 상속이 가능할 뿐만 아니라 다음과 같은 장점을 가질 수 있다.

 

1. 대규모 프로젝트 개발 시 일관되고 정형화된 개발을 위한 표준화가 가능하다.

2. 클래스의 작성과 인터페이스의 구현을 동시에 진행할 수 있으므로, 개발 시간을 단축할 수 있다.

3. 클래스와 클래스 간의 관계를 인터페이스로 연결하면 ,클래스마다 독립적인 프로그래밍이 가능하다.

 

 

인터페이스 레퍼런스를 통해 구현체를 사용하는 방법과 다중 상속

 1. 인터페이스는 인터페이스 끼리 상속을 받을 수 있으며 다중으로 상속이 가능하다 

  아래의 Human은 Dog, 와 Animals를 상속 받았으며 Human을 구현하는 Ugo클래스는 Dog,Aniamls,Human의 구현체     를 모두 구현해야한다.  

 2. 클래스 구현시 인터페이스의 일부만 구현을 원한다면 클래스를 추상 클래스로 선언해야한다.

 3. 인터페이스의 레퍼런스 타입에 따라 사용할 수 있는 구현체가  아래와 같이 달라질 수 있다.

 

interface Animals{
    public void eat();
    public void sleep();
}

interface Dog{
    public void bark();
}
//인터페이스는 인터페이스 끼리만 상속 가능하다.
interface Human  extends Dog,Animals{
    public void work();
}

class Monkey {
    public void eatBanana(){
        System.out.println("바나나를 먹는다");
    };
}

//Monkey 클래스를 상속받으면서 Human 인터페이스를 구현하였다.
class Ugo extends Monkey implements Human{
    @Override
    public void eat() {
        System.out.println("먹어요");
    }

    @Override
    public void sleep() {
        System.out.println("자요");
    }

    @Override
    public void bark() {
        System.out.println("짖어요");
    }

    @Override
    public void work() {
        System.out.println("일해요");
    }
}


public class InterfaceTest{
    public static void main(String[] args) {
        //Human 클래스는 dog 와 animal을 상속받았고
        //ugo는 Human 인터페이스를 구현 하였다.
        //타입에 따른 메서드 접근 가능 범위를 확인해보자
        //아래와 같이 레퍼런스에 따라 사용할 수 있는 구현체가 다르다
        Animals ugo = new Ugo();
        ugo.eat();
        ugo.sleep();

        Dog dogUgo = new Ugo();
        dogUgo.bark();

        Human humanUgo = new Ugo();
        humanUgo.work();
        humanUgo.bark();
        humanUgo.sleep();
        humanUgo.eat();
        
        //아래와 같이 Mokey 타입으로도 레퍼런스를 사용할 수 있다.
        Monkey monkeyUgo = new Ugo();
        monkeyUgo.eatBanana();
    }
}

 

 

인터페이스의 기본 메소드 default 메소드와 static 메소드 정의 [자바 8부터]

 자바 8부터 인터페이스의 정의가 몇가지 변경되었다. 

기존의 인터페이스는 추상메서드만 가질 수 있었지만 자바8 부터는 default 메서드와 static 메서드를 정의 할 수 있도록 

변경되었다. 인터페이스가 default 라는 키워드로 선언이 되면 인터페이스 내에서 이를 구현 할 수 있으며 이를 구현하는 클래스에서는 선언한 defalut 메서드를  overriding 할 수 있다.

 

default 메서드 생겨난 이유 

 

 인터페이스가 변경되면 그 인터페이스를 구현하고 있는 클래스는 모두 해당 메서드를 구현해야한다 .

이런 문제를 해결하기 위해 인터페이스에 메서드를 구현할 수 있도록 추가 되었다.

 

static 메서드가 생겨난 이유 

 

 인터페이스에서 static 메서드를 구현함으로 인터페이스 안에서 간단한 유틸리티성 메서드를 구현할 수 있게 되었다.

 

아래의 예제는 Calculator를 인터페이스에서 default 메서드와 static 메서드를 구현한 예제이다.

 

public interface Calculator {
    public int plus(int i , int j);
    public int multiple(int i , int j);
    //defalut 메서드의 선언 인터페이스 내에서 구현이 가능하다.
    default  int exec(int i , int j){
        return i + j ;
    }
    //static 메서드를 구현해도 오류가 발생하지 않는다.
    //static 메서드는 인터페이스명.메서드명 으로 사용이 가능하다.
    public static int exe2(int i , int j){
        return i*j;
    }
}

 

public class CalculatorImpl implements Calculator{
    @Override
    public int plus(int i, int j) {
        return i+j;
    }

    @Override
    public int multiple(int i, int j) {
        return i*j;
    }
}

class Test{
    public static void main(String[] args) {
        Calculator cal = new CalculatorImpl();
        cal.plus(3,4);
        cal.multiple(4,5);
        //인터페이스에서 직접구현한 default 메서드를 바로 사용할 수 있다.
        cal.exec(4, 3);
        //인터페이스에서 구현한 static 메서드 사용
        Calculator.exe2(3,4);
    }
}

 

 

자바 9부터는 private 메서드도 추가 되었다 .

 

  private 메서드는 인터페이스 내부 코드의 재사용 성을 향상시킨다. 예를 들어 2개의 기본 메서드에 공유되는 코드가 필요할 경우 private 메서드를 사용하여 이를 구현 할 수 있으며 , 구현하는 메서드에서는 이를 노출하지 않도록 할 수 있다.

 

인터페이스에서 private methods를 사용하는데에는 4가지 룰을 지켜야한다.

 

1. private interface methods는 추상 메서드일 수 없다.

2. private interface methods는 인터페이스 안에서만 사용가능하다.

3. private static methods는 다른 스테틱 혹은 non-static 인터페이스 메서드에서 사용될 수 있다.

4. private non-static methods는 private static 메서드 안에서 사용할 수 없다.

 

private 메서드 사용 예시

 

import java.util.function.IntPredicate;
import java.util.stream.IntStream;

public interface Calculator {

    default int addEvenNums(int ... nums){
        return add(n -> n%2 == 0, nums);
    }

    default int addOddNums(int ... nums){
        return add(n -> n%2 !=0, nums);
    }

    private int add(IntPredicate predicate , int ... nums){
        return IntStream.of(nums)
                .filter(predicate)
                .sum();
    }
}

 

람다로 구현되어 있으니 공부가 필요하다 .

 

 Calculator 인터페이스는 addEvenNums라는 숫자를 받아 짝수를 더하는 메서드와

 addOddNums 라는  홀수의 합을 구하는 메서드 두가지 default 메서드를 갖고 있으며 

addEvenNums와 addOddNums add라는 메서드를 공유하여 사용하고 있다 

이런 경우 위처럼 add 메서드를 private 메서드로 정의할 수 있다 .

add는 IntPredicate 타입과 , int 타입을 파라미터로 받아서 

IntStream을 열고 nums에서 filter를 통해 predicate에 해당하는 것만 찾아내고 더한다. 

 

위 인터페이스를 구현하고 사용하는 클래스의 예시이다.

 

public class MainTest implements Calculator{

    public static void main(String[] args) {
        Calculator  cal = new MainTest();

        int sumOfEvens = cal.addEvenNums(1,2,3,4,5,6,7,8,9);
        int sumOfOdds = cal.addOddNums(1,2,3,4,5,6,7,8,9);

        System.out.println(sumOfEvens);
        System.out.println(sumOfOdds);
    }
}

 

 MainTest에서 Calculator를 구현하였고 , 

메인 메서드에서  MainTest를 생성하고 Calculator 타입 변수에 담았다.

출력값은  짝수의 합인 20 과 , 홀수의 합인  25 가 나온다. 

요약

  짧게말해 java9의 private interface methods는 static 이거나 intance일 수 있다. 하지만 두 가지 경우 모두 ,

구현 클래스나 다른 인터페이스에 의해 상속될 수 없고 , 주로 인터페이스 내에서 코드의 재사용성을 높히기 위해서

사용되어 캡슐화를 향상시킨다.

 

www.tcpschool.com/ 에서 인터페이스 설명을 참조

programmers.co.kr/learn/courses/5/lessons/241 에서 인터페이스 default 메서드 static 메서드 참조

howtodoinjava.com/java9/java9-private-interface-methods/에서 인터페이스의 private 메서드 참조

반응형

'JAVA' 카테고리의 다른 글

JAVA 스터디 17 - ENUM  (0) 2021.03.25
JAVA 스터디 15 - 예외처리  (0) 2021.03.24
JAVA 스터디 13 - 접근제어자  (0) 2021.03.23
JAVA 스터디 12 - 패키지  (0) 2021.03.23
JAVA 스터디 11 - Object 클래스  (0) 2021.03.18