본문 바로가기
Web/JAVA

abstract class, Interface, generic

by 당진개발자 2024. 1. 19.

1. 추상 클래스  (확장의 개념)

- abstract 클래스는 상속 전용의 클래스

- 구현의 강제를 통해 프로그램의 안전성 향상

- 메서드의 선언부만 남기고 구현부는 세미콜론(;)으로 대체

- 구현부가 없다는 의미로 abstract 키워드를 메서드 선언부에 추가

- 객체를 생성할 수 없는 클래스라는 의미로 클래스 선언부에 abstract를 추가

※ Vehicle v = new DiselSuv(); 와 같이 하위 클래스의 객체 생성은 가능

- 반드시 자식 클래스에서는 추상 메서드를 오버라이딩 해야된다.

package com.ssafy.day5.a_abstract;

// 추상 클래스 (한개라도 추상 메서드가 있으면 추상클래스가 된다.)
public abstract class Vehicle {
	private int curX, curY;

	public void reportPosition() {
		System.out.printf("차종: %s: 현재 위치: (%d, %d)%n", this.getClass().getSimpleName(), curX, curY);
	}

	// 추상 메서드 >> 반드시 자식 클래스에서 오버라이딩 해야된다
	public abstract void addFuel();
}

 


 

2. Interface (공통 기능을 목적에 맞게 명세의 개념)

- 최고 수준의 추상화 단계 : 일반 메서드는 모두 abstract 형태

- JDK 8에서부터 default method와 static method 추가

- 클래스와 유사하게 interface 선언

- "모든 멤버변수는 public static final이며 생략 가능" >> 상속받지 못한다

- 모든 메서드는 public abstract이며 생략 가능

- 인터페이스는 다중 상속(extends)이 가능

- 클래스에서 implements 키워드를 사용해서 interface 구현 >> 모든 메서드를 오버라이딩 하거나 구현하지 않을 경우 abstract 클래스로 표시해야 함.

- 인터페이스와의 관계도 is a 관계이지만 좀 더 세부적으로 is able to라고도 한다. (ex. Object o = new IronMan();)

- 인터페이스 간 상속 시 오버라이딩 필요 X, 하지만 클래스에서 implements시 오버라이딩 필요 O

- 객체 생성 X

 


 

3. 인터페이스 필요성

- 구현의 강제로 표준화 처리 

- 인터페이스를 통한 간접적인 클래스 사용으로 손쉬운 모듈 교체 지원

- 서로 상속의 관계가 없는 클래스들에게 인터페이스를 통한 관계 부여로 다형성 확장

- 모듈 간 독립적 프로그래밍 가능 > 개발 기간 단축

 


 

4. default method

- 인터페이스에 선언 된 구현부가 있는 메서드

- default 제한자 추가 후 메서드 구현부 작성

- 인터페이스를 수정 시 모든 구현체들이 오버라이딩을 해야함 > default를 작성하면 구현 해야 할 필요가 없어짐 

- 추상 클래스를 사용하다가 요즘은 인터페이스와 default method를 사용하는 추세

- 부모 인터페이스와 부모 클래스와의 default method 충돌 시 super class가 우선

- 인터페이스에서 default method를 제공하고 다른 인터페이스에서도 같은 이름의 메서드가 있을 경우 sub 클래스는 반드시 오버라이드 해서 충돌 해결

 


 

5. 추상 클래스와 인터페이스 

 

6. generic

- 미리 사용할 타입을 명시해서 형 변환을 하지 않아도 되게 함.

- userNormalBox와 같이 사용할 경우는 사용시 마다 입력된 타입으로 형 변환을 해야한다.

- generic을 사용하면 객체의 타입에 대한 안전성 향상 및 형 변환의 번거러움 감소

- 타입 파라미터 > T : reference Type, E : Element, K : Key, V : Value

public class NormalBox {
	private Object some;

	public Object getSome() {
		return some;
	}
	public void setSome(Object some) {
		this.some = some;
	}
}
public class GenericBox<T> { 
	private T some;

	public T getSome() {
		return some;
	}
	public void setSome(T some) {
		this.some = some;
	}
}
private static void useNormalBox() {
    // TODO: NormalBox 타입의 객체를 생성하고 사용해보세요.
    NormalBox box = new NormalBox();
    box.setSome("Hello");
    Object some = box.getSome();
    if (some instanceof String data) {
        data = (String)data;
        System.out.println("data : " + data);
    }
    // END
}

private static void useGenericBox() {
    // TODO: GenericBox 타입의 객체를 생성하고 사용해보세요.
    GenericBox<String> box = new GenericBox<>();
    box.setSome("hello");
    String data = box.getSome();
    System.out.println(data);
    // END
}

 

- generic 메서드

※ 객체 생성 시점에 T의 타입 결정, 메서드 호출 시점에 P의 타입 결정

public <P> void method1(P p) {
        System.out.printf("클래스 레벨의 T: %s%n", some.getClass().getSimpleName());
        System.out.printf("파라미터 레벨의 P: %s%n", p.getClass().getSimpleName());
}

tpmt.method1(10);

 


 

7. type parameter 제한 

- extends 키워드를 사용하여 Number 클래스를 상속받는 클래스만 사용 가능

(ex. NumberBox<String> box = new NumberBox<>();) >> 오류 발생

public class NumberBox<T extends Number> {
	private T some;

	public T getSome() {
		return some;
	}
	public void setSome(T some) {
		this.some = some;
	}
}

 

 


 

8. Generic Type 객체를 할당 받을 때 와일드 카드(?) 이용

- 제네릭은 형변환이 되지 않는다.

PersonBox<Person> pPer2 = new PersonBox<SpiderMan>(); // 불가능

 

- 객체를 할당 받을 때 와일드 카드를 이용하면 형 변환 처럼 사용이 가능하다. (type parameter은 다르다.)

- type parameter는 ?가 불가능 하다.

public class WildTypeTest {
    @SuppressWarnings("unused")
    public void wildCardTest() {
        PersonBox<Object> pObj = new PersonBox<>();
        PersonBox<Person> pPer = new PersonBox<>();
        PersonBox<SpiderMan> pSpi = new PersonBox<>();

        // 어떤 타입이 오던지 다 받아줄 수 있다.
        PersonBox<?> pAll = pPer;
        pAll = pSpi;
        pAll = pObj;

        // Person 또는 상속받은 경우만 받아줄 수 있다.
        PersonBox<? extends Person> pChildPer = pPer;
        pChildPer = pSpi;
        // pChildPer = pObj;

        // Person 또는 조상만 받아줄 수 있다.
        PersonBox<? super Person> pSuperPer = pPer;
        // pSuperPer = pSpi;
        pSuperPer = pObj;
    }
}

class Person {
}

class SpiderMan extends Person {
}

class PersonBox<T> {
}