📌 1. 인터페이스(Interface)란?
인터페이스란, 클래스들이 필수로 구현해야 하는 추상 자료형입니다. 객체의 사용방법을 가이드라인 하는 것이라고 생각하면 됩니다.
모든 메소드가 구현된 부분이 없는 추상 메소드 형태로 이루어진 클래스. 상속의 관계가 없는 클래스 간의 공통되는 로직을 구현하여 사용할 수 있습니다.
자바의 인터페이스는 추상 메서드와 상수로만 이루어져 있습니다. Implements 키워드를 사용합니다. "추상 메서드의 강제구현" 이라는 의미가 강합니다. 내부의 모든 필드가 public static final로 정의됩니다.
📌 2. 인터페이스의 특징은?
1. 다중 상속 가능
-- > 인터페이스는 껍데기만 존재하여 클래스 상속 시 발생했던 모호함이 없습니다. 고로 다중 상속이 가능합니다.
인터페이스는 별도의 다른 클래스를 상속받을 수 있습니다.
2. 추상 메서드와 상수만 사용 가능
- > 인터페이스에는 구현 소스를 생성할 수 없습니다. 고로 상수와 추상 메서드만 가질 수 있습니다.
3. 생성자 사용 불가
- > 인터페이스 객체가 아니므로 생성자를 사용하실 수 없습니다.
4. 메서드 오버라이딩 필수
- > 자식 클래스는 부모 인터페이스의 추상 메서드를 모두 오버라이딩해야 합니다.
📌 3. 추상 클래스와 무엇이 다를까?
추상클래스는 트리 형식으로 상위 클래스와 하위 클래스로 나뉘어지는 트리 구조입니다.
하위 클래스는 상위 클래스에서 추상화시킨 생성자 혹은 메서드를 정의하면서 더 구체화되어간다.
반면에, 인터페이스는 트리 구조와 같은 수직적 구조가 아닌 수평적 구조를 가지게 된다.
📌 4. 인터페이스 사용 이유는?
추상 클래스를 통해 객체들 간의 네이밍을 통일할 수 있고 이로 인해 소스의 가독성과 유지보수가 향상됩니다.
확장에는 열려 있고 변경에는 닫혀 있는 객체 간 결합도(코스 종속성)를 낮춘 유연한 방식의 개발이 가능합니다.
인터페이스는 코드와 인터페이스로 생성된 객체들을 중간에 중계해주는 역할을 합니다. 개발 코드가 먼저 인터페이스의 메서드를 호출하면 인터페이스는 객체의 메서드를 호출시키는 형식입니다. 인터페이스로 구현된 객체는 인터페이스에서 정의된 추상 메서드와 동일한 메소드 이름, 매개 타입, 리턴 타입을
가진 실체 메서드를 필수로 가지고 있어야 합니다. 이러한 과정을 통해 클래스 간의 결합도(코드 종속성)을 낮춘 효율적인 프로그래밍을 할 수 있습니다.
인터페이스가 코드 종속성을 낮춘다?
- 코드 종속성은 각각의 메서드 간의 결합도를 의미하며 인터페이스를 활용하면 한 메서드를 수정하였을 때, 다른 메서드도 수정해야 하는 상황을 줄여준다는 의미입니다. 인터페이스로 추상 메서드를 지정하면 메서드의 input값과 output값이 고정되어 있습니다. 예를 들자면 public abstract String myName(String name)이라는 메서드는 input값, output값 모두 String으로 고정되어 있습니다. 이 메서드를 구현하는 객체에서 아무리 수정하라도 input 값과 output 값은 String으로 고정되어 있어 변경에 대한 영향도가 작습니다. 그래서 인터페이스는 "변경에 강하다." "확장에는 열려있고 변경에는
닫혀있다."고 말하는 것입니다.
📌 5. 자바 인터페이스의 사용법?
Interface라는 키워드를 붙여 인터페이스를 만들면 implements라는 키워드를 통해 객체들을 구현하는 용도로만 사용 가능합니다. 또한 인터페이스에는 구체적인 대상을 생성할 수 없고 오로지 상수와 추상 메서드만 사용할 수 있습니다.
📌 6. 인터페이스끼리의 상속?
인터페이스끼리도 상속을 통해 확장을 시켜나갈 수 있고 하위 인터페이스에서 네이밍을 강제하여 객체 간의 통일성을 추구할 수 있습니다. 인터페이스로 객체를 구현할 때는 Implements라는 키워드를 쓰지만 인터페이스끼리 상속을 할 때는 클래스와 마찬가지로 extends 키워드를 사용합니다.
📌 7. 인터페이스는 왜 사용할까?
인터페이스에는 상속과 추상 메서드가 있기 때문에 오히려 개발의 복잡성을 높이지 않을까라는 의문이 있지만 필요에 의해서 만들어낸 기능입니다.
개발자들이 서로 협업하여 프로젝트를 만들 동안 서로 만들어낼 기능들은 유기적으로 상관관계를 만들어낼 것입니다.
A 개발자가 만들고 있는 A 클래스는 B 개발자가 만들고 있는 B클래스가 완성이 되어야지 개발이 가능한 경우가 있을 것입니다.
이런 경우, A 개발자는 B클래스의 더미(dummy) 클래스라는 가짜 혹은 임시 클래스를 만들어서 B클래스가 필요한 자리에 채워 넣은 뒤, B 개발자가 B 클래스를 완성한 후에 바꿔짐으로써 개발 일정에 차질이 생기지 않을 수 있을 것입니다.
B 클래스에는 여러 기능을 하는 메서드들이 존재하는 데 이 메서드들의 형식(틀)을 담당하는 것이 인터페이스입니다. B 더미 클래스와 B 클래스는 같은 인터페이스 C를 사용하고 있고 인터페이스의 메서드 1과 2는 추상 메소드로서 형식만 갖추어져 있습니다. B개발자가 B클래스에서 메소드 1과 메소드 2를 정의하고 B 클래스를 완성한다면, A 개발자가 B 더미 클래스를 B 클래스로 교체해주면 됩니다.
📌 8. 인터페이스를 사용하지 않는다면?
인터페이스로 메서드 1과 메서드 2의 형식을 잡아 놓지 않는다면, 개발자들 간의 B클래스를 담당하는 B개발자가 메서드 1을 A 개발자가 가지고 있는 B 더미 클래스 안의 메서드 1의 형식이 맞지 않아서 많은 양을 리팩터링 해야 하는 경우가 발생할 수 있습니다.
-- > 인터페이스는 메서드의 틀을 미리 만들어 개발자 간의 의사소통 혼선을 줄여주고 다형성 개발에 유리함을 가져다주는 객체입니다.
📌 9. 인터페이스 간단 예시
interface D extends A, B{} : interface D는 interface A와 B를 모두 확장합니다.
class K extends E implements A, B {} : class K는 클래스 E를 상속하고 인터페이스 A와 B를 구현합니다.
class G extends E {} : 클래스가 클래스를 상속할 때는 단일 상속만 가능합니다. class G는 클래스 E를 상속합니다.
class J implements A {} : 클래스 J가 인터페이스 A를 구현합니다.
위의 코드는 인터페이스를 구현한 클래스 'Sub'을 사용하는 예시입니다.
class Sub implements Method { } : Sub 클래스는 Method 인터페이스에 선언된 모든 메서드를 구현해야 합니다.
@Override
public void one() { } : Sub 클래스에서는 one과 two 메서드를 오버라이딩하여 재정의하고 있습니다.
Method m = new Sub(); : 인터페이스는 인스턴스 생성이 불가능하므로 Method m = new Sub(); 와 같이 인터페이스를 구현한 클래스의
인스턴스를 생성하여 참조변수로 사용할 수 있습니다.
m.one(); m.two(); : m 참조변수를 통해 one() 과 two() 메서드를 호출하고 있습니다. 실제 실행되는 메서드는 Sub 클래스에서 오버라이딩한 메서드입니다.
📌 10. 인터페이스 예제
위 코드는 Vehicle 클래스를 상속받아 'Car'와 'Yacht' 클래스를 정의하고 있습니다.
private int number; : 'private' 접근 지정자를 사용하여 클래스 외부에서 직접 접근할 수 없도록 보호하고 있습니다. int 형식으로 선언되어 있습니다.
Vehicle(int number) : 생성자 메서드로 number를 매개변수로 받아서 인스턴스가 생성될 때 number 필드를 초기화합니다. 이 생성자는 객체가 생성될 때 반드시 호출되어야 하며, Vehicle 클래스의 인스턴스를 생성할 때 필요한 초기화 작업을 수행합니다.
getNumber() : number 필드의 값을 반환하는 메소드입니다. public 접근 지정자를 사용하여 클래스 외부에서 호출할 수 있도록 공개 되었습니다.
Car 클래스는 number 필드와 getNumber() 메서드를 가지고 있습니다. Car(int number) 생성자는 Vehicle 클래스의 생성자를 호출하여 number 필드를 초기화합니다. super(number)를 통해 부모 클래스인 Vehicle 클래스의 생성자를 호출합니다. Car 클래스의 인스턴스가 생성될 때 number 필드가 초기화됩니다. drive() 메서드는 자동차가 고속도로를 달리는 기능을 나타냅니다. super.getNumber() 를 통해 부모 클래스인 Vehicle 클래스의 getNumber 메서드를 호출하여 자동차의 등록번호를 호출하고 있습니다.
Yacht 클래스는 Vehicle 클래스를 상속 받습니다. number 필드와 getNumber() 메서드를 상속 받습니다. Yacht(int number) 생성자는 Vehicle 생성자를 호출하여 number 필드를 초기화합니다. 이를 통해 요트의 등록 번호를 설정할 수 있습니다. sail() 메서드는 요트가 향해하는 기능을 나타냅니다. 이 메서드는 System.out.println()을 사용하여 요트의 등록 번호를 출력하고 요트가 한강 위를 향해하는 메시지를 출력합니다.
Vehicle[] myVeicles = new Vehicle[2]; : 크기가 2인 Vehicle 배열을 생성하고 이 배열에 Car와 Yacht 인스턴스를 각각 저장합니다.
info(myVehicles[0]); info(myVehicles[1]); : info() 메서드는 Vehicle 타입의 매개변수를 받아서 해당 차량의 정보를 출력합니다.
if(vehicle instanceof Car) { } : instanceof 연산자를 사용하여 매개변수로 받은 vehicle 이 Car 클래스의 인스턴스인지, Yacht 클래스의 인스턴스인지를 확인합니다.
Car car = (Car) vehicle; car.drive(); : vehicle 이 Car 클래스의 인스턴스인 경우, Car 클래스로 형변환하여 drive() 메서드를 호출하여 자동차의 정보를 출력합니다.
Yacht yacht = (Yacht) vehicle; yacht.sail(); : vehicle이 Yacht 클래스의 인스턴스인 경우, Yacht 클래스로 형변환하여 sail() 메서드를 호출하여 요트의 정보를 출력합니다.
'JAVA' 카테고리의 다른 글
[JAVA] Static, Stack, Heap | Java 메모리 영역의 구조와 특징 (0) | 2024.04.23 |
---|---|
[JAVA] JVM(자바 가상머신) 과 메모리 구조 (0) | 2024.04.23 |
[JAVA] 추상 클래스, 추상 메소드 (Abstract Class, Abstract Method) (4) | 2024.04.18 |
[JAVA] Polymorphism (다형성) 이용하여 여러 개의 객체를 구현하기 (0) | 2024.04.17 |
[JAVA] Random 구문을 이용하여 조건식에 맞는 정수값 출력하기 (0) | 2024.04.16 |