인터페이스

dev_314·2022년 11월 7일
0

자바 라이브 스터디

목록 보기
13/18

| 백기선님의 라이브 스터디를 참고하여 작성한 게시물입니다.

인터페이스 기본

vs abstract class

인터페이스는 일종의 abstract class로 볼 수 있다.
그러나 abstract class와 달리,

1. body가 있는 method를 선언할 수 없다.
2. instance variable를 가질 수 없다.

인터페이스 정의하기

인터페이스는 다음과 같은 형태를 띤다.

public inteface MyInterface {

	public static final int STATIC_VARIABLE = 10; // instance variable를 가질 수 없다.
  
  	public abstract void myMethod(int parma1); // body가 있는 method를 선언할 수 없다.
}

인터페이스 구현하기

인터페이스를 다음과 같이 구현한다.

public class MyClass implements MyInterface {
	
    @Override
    public void myMethod(int param1) {
		int plus = STATIC_VARIABLE + 1;
		...
    }
}

@Override annotation은 컴파일러에게 '구현 클래스에서 인터페이스의 추상 메소드를 구현한다'라고 알려주는 기능이다.
구현할 때 메소드 시그네쳐가 다른경우 컴파일 타임에 에러가 발생한다.
생략 가능하지만, 사용을 권장한다.

인터페이스를 일부만 구현할 수도 있다. 이런 경우에는 구현하는 class에 abstract를 사용해야 한다.

public inteface MyInterface {

	public abstract void method1();
	public abstract void method2();
}
public abstract class MyClass implements MyInterface {

	@Override
    public void method1() {
    	...
    }
}

인터페이스와 접근제한자

맨날 IDE로 인터페이스를 자동 생성하다 보니, 인터페이스의 접근제한자를 생각해 본 적이 없더라

결론부터 말하면 인터페이스는 default 또는 public이어야 한다. (클래스도 마찬가지)

인터페이스가 등장한 이유가 나를 따라 구현해주오이기 때문이다. 즉, 모든 위치에서 인터페이스를 사용할 수 있으려면 public일 수 밖에 없다.

의문: 그렇다면 왜 defualt는 가능한 것인가? (default는 제한이 있는디... 그 정도는 그냥 허용해 주는 건지 모르겠다)

인터페이스 변수, 메소드와 접근제한자

변수

참고
Why does Java allow static final variables in interfaces when they are only intended to be contracts?

기본적으로 인터페이스에서는 상수만을 정의할 수 있다.
즉, 인터페이스의 변수는 무조건 public static final이어야 한다.

public static final은 생략할 수 있는데, 생략한 경우 컴파일 타임에 컴파일러가 자동으로 입력해준다

why static

인터페이스 변수는 인스턴스를 생성할 수 없다. 즉, 개별 인스턴스마다 각각의 변수값을 가질 수 없다.
static을 사용해서 이러한 특징을 강화했다. (뇌피셜)

why final

여러 class에서 인터페이스를 구현할 수 있는데, final이 아니면 각 class에서 지들 마음대로 변경할 수 있다.
이를 막기 위해 final을 사용해서 불변(상수)로 만들었다. (역시나 뇌피셜)

why public

변수가 private이면 인터페이스를 구현하는 다른 클래스, 또는 패키지에서 해당 변수를 사용할 수 없기 때문 (허허 뇌피셜)

package myinterface;

interface MyInterface {

    int MY_NUMBER = 10; // 컴파일러가 public static final을 붙여준다.
    private int MY_NUMBER = 10; // Error : Modifier 'private' not allowed here
    
}

IntelliJ의 기능인지는 모르겠지만, public static final을 입력하면 해당 키워드는 생략 가능함을 보여준다.

메소드

인터페이스의 메소드는 항상 public abstract이어야 한다.

public abstract도 역시 생략할 수 있다.

why public abstract

public은 변수와 같은 이유일 것이고, abstract는 인터페이스의 탄생 배경에서 비롯된 것으로 추측된다(brain피셜)

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

인터페이스를 구현하는 클래스

inteface MyInterface {
	void commonMethod();
}
class MyClass implements MyInterface {
	
    @Override
    public void commonMethod() {
    	...
    }
    
    public void myMethod() {
    	...
    }
}
class Main {
	
    public static void main(String[] args) {
    	MyInterface instance = new MyClass(); // upcasting
        
        instance.commonMethod();
        // instance.myMethod();
        ((MyClass) instance).myMethod(); // down casting
    }
}

Annonymous Object

참고
[Java] 인터페이스와 추상 클래스는 진짜 객체 생성이 불가능한가? 익명 객체(Anonymous Object)로 객체 생성하기

인터페이스 타입의 인스턴스를 생성은 불가능하다. 메소드가 abstract하기 때문이다.

그런데 익명 객체를 사용하면 일회용 인스턴스를 만들 수 있다.

abstract class도 익명 객체를 통해 일회용 인스턴스를 만들 수 있다.

Q: 알겠는데, 이 기능을 어디에다 씁니까...
A: 흠흠,,,

Compartor 구현

import java.util.Arrays;
import java.util.Comparator;

public class MyInt {

    int value;

    public MyInt(int weirdNumber) {
        this.value = weirdNumber;
    }

    public static void main(String[] args) {
        MyInt[] numbers = {
                new MyInt(1),
                new MyInt(2),
                new MyInt(3),
                new MyInt(3),
                new MyInt(4),
                new MyInt(5)
        };

        Arrays.sort(numbers, new Comparator<MyInt>() {
            @Override
            public int compare(MyInt o1, MyInt o2) {
                return o1.value - o2.value;
            }
        });

        for (MyInt number : numbers) {
            System.out.print(number.value + " ");
        }
    }
}

Comparator는 다음과 같은 '인터페이스'

public interface Comparator<T> {
	...
}

인터페이스 더 나아가기

인터페이스 상속

인터페이스는 인터페이스만 상속받을 수 있다.
인터페이스는 다중 상속이 가능하다.

interface mother {
	void motherMethod();
    void parentMethod();
}

interface father {
	void fatherMethod();
    void parentMethod();
}

interface child extends mother, father {
	void childMethod();
}

아래와 같이 구현한다.


class ImpleClass implements child {
  	@Override
    public void childMethod() {
        System.out.println("childMethod");
    }

    @Override
    public void fatherMethod() {
        System.out.println("fatherMethod");
    }

    @Override
    public void motherMethod() {
        System.out.println("motherMethod");
    }

    @Override // 과연 누구의 메소드일까
    public void parentMethod() {
        System.out.println("parentMethod");
    }
    
    public static void main(String[] args) {
        Child imple = new Imple();
        imple.childMethod();
        imple.motherMethod();
        imple.fatherMethod();
        imple.parentMethod();

        Mother imple1 = imple; // upcasting
        Father imple2 = imple; // upcasting

        System.out.println();

        imple1.parentMethod();
        imple2.parentMethod();
    }
}

Q: parentMethod는 어떤 인터페이스의 메소드를 override한 것일까?
A: 중요하지 않다.

인터페이스의 기본 메소드 (Default Method), 자바 8

위에서 설명한 것 처럼, 인터페이스에 선언된 메소드는 method body를 가질 수 없다.
그런데 자바8(1.8)부터 이 규칙이 깨졌다.
default method를 사용하면 인터페이스에서도 method body를 가진 메소드를 선언할 수 있다.

interface MyInterface {
	void basicMethod(); // 기본적으로는 body를 가질 수 없다.
    
    void default defaultMethod() {
    	System.out.println("this is default method");
    }
}

기능의 구현보다, 기능의 선언에 초점을 맞춰 사용하는 인터페이스에 왜 이러한 기능이 필요할까?

참고
자바의 Default Method (디펄트 메소드)

만약, A 인터페이스가 존재하고, A 인터페이스를 구현하는 클래스가 엄청 많이 존재한다고 생각해보자.
이런 상황에서 A 인터페이스에 새로운 메소드를 선언하면, 구현체들은 전부 A 메소드를 override해야 할 것이다.(또는 abstract처리)
이는 매우 호환성이 떨어지는 상황(?)이라고 할 수 있다. 이런 상황을 예방하고자 default method가 등장한 것이다

default method도 역시나 구현체에서 override할 수 있다.
default method는 반드시 override해야하는 건 아니다.

뇌피셜 주의

구현체에서 인터페이스의 default method를 override하지 않으면, 인터페이스에서 구현된 default method가 그대로 실행된다.
즉, 초기에 선언된(default) 구현 내용이 그대로 실행되기 때문에 이러한 이름이 붙지 않았을까....?

인터페이스의 static 메소드, 자바 8

default method뿐만 아니라, 자바 8에서 이뤄진 인터페이스의 큰 변화로 static 메소드의 등장을 꼽을 수 있다.

생각해 보면, static 메소드를 굳이 인터페이스에서 못 쓰게 할 이유가 없었다. 그래서 생긴것 같다.

static이기 때문에, 다음과 같이 사용해야 한다.

선언

interface MyInterface {
	static void printWelcomMessage() {
    	System.out.println("hihi");
    }
}

호출

class MyClass {
	public static void main(String[] args) {
    	MyInterface.printWelcomMessage();
    }
}
  1. static method는 무조건 method body가 있어야 한다. (어쩌면 당연)
  2. static method는 override 불가능 (어쩌면 당연2)

인터페이스의 private 메소드, 자바 9

default의 한계

참고
[Java-30] java8 과 java9 의 인터페이스 변화

다음과 같은 default, static 메소드가 존재한다.

interface MyInteface {
	
    default String getMessage(String name) {
    	return "hi " + name;
    }
    
    default void print(String name) {
    	// sout == System.out.println
        sout(getMessage());
    }
}

이를 사용해보자

{
	MyInterface.print("wonju");
}
  1. getMessage는 print 내부에서만 사용되는 메소드인데, 외부에 노출됨(무조건 public일 수 밖에 없음)
  2. 구현체에서 getMessage를 override할 가능성 존재

private으로 해결하기

자바9 부터 인터페이스에서도 private method를 선언할 수 있다.
private은 구현체에서 접근할 수 없기 때문에, private method는 method body를 가져야 한다.

private을 사용해서 개선해보자

interface MyInteface {
	
    private String getMessage(String name) {
    	return "hi " + name;
    }
    
    default void print(String name) {
    	// sout == System.out.println
        sout(getMessage());
    }
}
  1. 구현체에서 getMessage를 확인할 수 없다 -> override 방지

인터페이스 관련 이것저것

  1. enum이 등장한 자바 5.0 이전에는 인터페이스를 통해 상수를 관리했다고 한다.
interface Date {
	public static final MON = 0;
	public static final TUE = 1;
    ...
}
profile
블로그 이전했습니다 https://dev314.tistory.com/

0개의 댓글