728x90
230725 객체지향 프로그래밍 - 인터페이스, 제네릭
생성 일시: 2023년 7월 24일 오후 8:22
- 인터페이스
- 인터페이스 vs 클래스
- Generic
인터페이스 Interface
인터페이스란?
서로 다른 장치들을 연결시켜 주는 규격
인터페이스
- 완전히 추상화된 설계도
- 클래스와 유사하게 작성이 가능 (class 대신 interface 키워드 사용)
- 모든 메서드가 추상 메서드 (JDK8부터 default method, static method 추가)
public interface 인터페이스이름 {
public static final 타입 상수이름1 = 10;
(public static final 생략가능) 타입 상수이름 상수이름2 = 10;
public abstract void 메서드이름1(타입 매개변수...);
(public abstract 생략가능) void 메서드이름2(타입 매개변수...);
}
인터페이스 구현
- 인터페이스는 그 자체로 인스턴스를 생성할 수 없음 (메서드 자체에 구현부 존재 x)
interface MyInterface {} public class Main { MyInterface m = new MyInterface(); --> 이거 안됨 }
- 인터페이스 내에 있는 메서드를 구현할 클래스가 필요함
extends
대신implements
키워드를 사용하여 구현 클래스 작성interface Shape{} class Circle extends Shape{} --> (X) Circle extends Shape{} --> 이건 또 된다는 듯 class Circle implements Shape{} --> 여러 개의 interface implements 가능
- 인터페이스를 구현하는 구현 클래스로 사용하겠다
- 인터페이스는 그 자체로 미완성된 설계도이므로 우리가 인스턴스를 생성할 수 없음
- extends 대신에 implements 라고 하는 키워드를 이용한다
package interface01;
public abstract MyClass implements MyInterface {
@Override
public void method1() {
System.out.println("메소드1이야");
}
@Override
public void method2() {
System.out.println("메소드2야");
}
}
- 인터페이스를 구현하는 클래스는 추상 메소드를 반드시 오버라이딩(재정의)해야 한다.
(구현하지 않을 경우abstract
클래스로 표시해야 함)
interface Chef {
void cook();
}
class KFoodChef implements Chef {
@Override
public void cook() {
System.out.println("한식을 요리한다.");
}
// or 이렇게 작성
abstract class JFoodChef implements Chef {
}
- 다형성 적용
public static void main(String[] args) { Chef chef = new KFoodChef(); }
인터페이스 상속
extends
를 이용하여 상속이 가능- 다중 상속이 가능 (클래스의 다중 상속에서의 문제점 없음)
→ 왜? 메소드가 겹치더라도 최종 구현 부분은 구현 객체에서 이루어지기 때문에
(클래스는 다중 상속이 안 되는 이유가 이름이 같을 경우 모호성을 해결하지 못해서 였음~) class Robot implements Chef { @Override public void move() {} @Override public void cook() {} }
interface Movable { public abstract void move(); } interface Cookable { public abstract void cook(); } interface Chef extends Movable, Cookable { }
인터페이스의 필요성
- 표준화(규격) 처리 가능
- 개발 기간 단축 가능
- 서로 관계가 없는 클래스들에게 인터페이스를 통해 관계를 맺을 수 있음
- 인터페이스를 통한 간접적인 클래스 사용으로 손쉬운 모듈 교체 지원
- 독립적 프로그래밍 가능
default
method
- 인터페이스에 구현부가 있는 메서드를 작성할 수 있음
- 매번 재정의하기 귀찮은 메서드가 있으니 override 하지 않고 미리 정의해 둘 수도 있음!
- 메서드 앞에
default
라는 키워드를 작성해야 함 public
접근제한자를 사용해야 생략 가능
interface Chef extends Movable, Cookable {
public void info();
}
★ 조상 클래스에도 동일한 이름의 method가 있다면 충돌! → 그래서 인터페이스의 default method 가 우선, 그냥 구현부에서 override하면 됨
⬇️
interface Chef extends Movable, Cookable {
public default void info() { /* 구현 내용 */ };
}
static
method
- 클래스의 static 메서드와 사용방법이 동일함
- 인터페이스 이름으로 메서드에 접근하여 사용
interface Chef extends Movable, Cookable { public default void info() { /* 구현내용 생략 */ }; public static void sound() { System.out.println("위잉~ 치킨"); }; } public class RobotTest { public static void main(String[] args) { Chef.sound(); } }
인터페이스 vs 클래스
클래스와 인터페이스 비교
클래스 | 인터페이스 | |
---|---|---|
특징 | class 키워드를 사용하여 정의 필드와 메서드, 생성자로 이루어짐 | interface 키워드를 사용하여 정의 상수와 추상 메소드 (메소드 선언부) 로 이루어짐 |
public static final 생략 | public abstract 생략 | |
관계 | 인터페이스를 구현함 | 클래스에 의해 구현됨 |
멤버 변수 | 선언 가능 | 상수만 가능 |
다중 상속 | 클래스는 하나의 클래스만 상속 가능 | 인터페이스는 여러 개의 인터페이스 상속 가능 (구현부가 없으므로 헷갈리지 않음) |
다중 구현 | 클래스는 여러 개의 인터페이스를 다중으로 구현 (implements) 가능 | |
인스턴스 | 생성 가능 | 생성 불가 |
타입 | 타입으로 사용 가능 | 타입으로 사용 가능 |
추상클래스와 인터페이스 비교
공통점: 특정 기능의 구현을 강제하고 싶을
추상 클래스 | 인터페이스 | |
---|---|---|
객체생성 | 불가 | 불가 |
일반 메소드 | 가능 | 불가 |
일반 필드 | 가능 | 불가 (상수만 가능) |
메서드 | abstract를 붙여야만 추상 메소드 | 모든 메서드는 추상 메서드 |
사용 | - 추상적인 클래스의 성격을 가질 때 (일부 메서드만 미완성인 설계도) | |
- 서로 유사한 클래스 사이에 코드를 공유하고 싶을 | - 서로 관련없는 클래스 사이에 공통으로 적용되는 인터페이스를 구현하기를 원할 때, ex Comparable, Serializable | |
- 객체(클래스)의 성격이라기보다 어떤 기능을 구현하고 있다는 약속의 성격이 있을 때 |
Generic
제네릭
- 다양한 종류의 객체들을 다루는 메서드나 컬렉션 클래스에서 컴파일 시 타입을 체크해주는 기능
- 객체의 타입 안정성을 제공한다
- 형변환의 번거로움이 없어지므로 코드가 간결해진다
제네릭 클래스
- 클래스를 정의할 때
- 클래스 안에서 사용되는 자료형(타입)을 구체적으로 명시하지 않고
- T와 같이 타입 매개변수를 이용하는 클래
제네릭 클래스 선언
- 클래스 또는 인터페이스 선언 시 <>에 타입 파라미터 표시
public class ClassName<T>{} public interfacec InterfaceName<T>{}
- 타입 파라미터 → 특별한 의미의 알파벳보다는 단순히 임의의 참조형 타입을 말함
- T: reference Type
- E: Element
- K: Key
- V: Value
제네릭 클래스 객체 생성
- 변수와 생성쪽의 타입은 반드시 일치해야 함 (상속관계에서도 마찬가지)
Box<Student> box = new Box<Student>(); // (O) Box<Person> box = new Box<Student>(); // (X)
- 추정이 가능한 경우 타입 생략 가능 (생성자 쪽 생략 가능 JDK 1.7부터)
Box<Student> box = new Box<>();
- 제네릭 타입을 지정하지 않고 생성이 가능하지만 권장 X (자동으로 T는 Object)
제네릭 클래스 실습
class Box {
private Object obj;
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
}
public class BoxText {
public static void main(String[] args) {
Box box = new Box();
box.setObj("Hello");
box.setObj(new Person());
Object obj = box.getObj();
if (obj instanceof Person) {
Person p = (Person) obj;
System.out.println("사람");
} else if (obj instanceof String) {
String str = (String) obj;
System.out.println("문자열");
} else {
System.out.println("알 수 없음");
}
}
}
class Box<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
public class BoxTest {
public static vodi main(String[] args) {
Box<Person> box1 = new Box<>();
box1.setObj(new Person());
Person p = box1. getObj();
System.out.println("사람");
Box<String> box2 = new Box<>();
box2.setObj("Hi");
String str = box2.getObj();
System.out.println("문자열");
}
}
제한된 제네릭 클래스
- 타입 문자 사용할 타입을 명시하였지만 역시 모든 타입을 사용할 수 있는 문제가 발생
- 구체적인 타입의 제한이 필요할 때
extends
키워드를 사용할 수 있음 (Person의 자손의 타입지정 가능) class Box<T extends Person> { private T obj; public T getObj() { return obj; } public void setObj(T obj) { this.obj = obj; } }
- 클래스가 아닌 인터페이스로 제한할 경우도
extends
키워드 사용 - 클래스와 함께 인터페이스 제약 조건을 이용할 경우 &로 연
와일드 카드 이용
- generic type에서 구체적인 타입 대신 사용
표현 | 설명 |
---|---|
Generic type<? > | 타입에 제한이 없음 |
Generic type<? extends T> | T와 T를 상속받은 타입들만 사용 가능 |
Generic type<? super T> | T와 T의 조상 타입만 사용 가능 |
실습 — 파일 구조
UserManager
→ iUserManager : 쓸 메소드들을 정의만 함
→ UserManagerImpl : 인터페이스를 구현
728x90
'Java > 기본 문법' 카테고리의 다른 글
JAVA 객체지향 프로그래밍 - Collection (0) | 2023.08.12 |
---|---|
JAVA 객체지향 프로그래밍 - 다형성 (0) | 2023.08.10 |
JAVA 객체지향 프로그래밍 - 상속 (0) | 2023.08.10 |
JAVA 객체지향 프로그래밍 - 객체 (0) | 2023.08.09 |
JAVA 객체지향 프로그래밍 - 클래스와 메소드 (0) | 2023.08.09 |