본문 바로가기

Java

Java- 제네릭(1)

제네릭


 제네릭이란?

  제네릭은 대충 봤을 때는 템플릿이랑 똑같다. 근데 완전히 똑같진 않고 살짝 차이가 있긴한데 이건 나중에 얘기하자.

 제네릭 클래스의 생성

class Orange{
    @Override
    public String toString(){
        return "this is an Orange";
    }
}

class Apple {
    @Override
    public String toString(){
        return "this is an Apple";
    }
}

class Box<T>{  //제네릭 클래스
    private T ob;
    Box(T ob){
        this.ob = ob;
    }
    void showInfo(){
        System.out.println(ob);
    }
    T get(){
        return ob;
    }

}

public class FruitAndBox_Generic {
    public static void main(String[] args) {
        Box<Apple> appleBox = new Box<Apple>(new Apple());
        System.out.println(appleBox.get());

        Box<Orange> orangeBox = new Box<Orange>(new Orange());
        System.out.println(orangeBox.get());
    }
}

this is an Apple
this is an Orange

위의 코드는 충분히 쉽게 이해 가능하다.

 

 용어 정리

  • 타입 매개 변수(Type Parameter) :        Box<T>에서 T 
  • 타입 인자(Type Argument) :        Box<Apple> 에서 Apple                       
  • 매개변수화 타입(Parameterized Type) :        Box<Apple>

 

 기본 자료형은 타입 매개 인자로 사용할 수 없다

Box<int> box = new Box<int>();
//타입 인자로 기본 자료형이 올 수 없으므로 컴파일 오류 발생

  타입 인자로는 기본 자료형을 사용할 수 없다. 그래서 위와 같은 문장의 구성은 불가능하다. 하지만 기본 자료형에 대한 래퍼 클래스가 존재하므로 이를 대신 사용할 수는 있다.

 

Box<Integer> integerBox = new Box<>();  

integerBox.set(123);  //오토 박싱
integerBox.get(); //오토 언박싱

  위와 같은 코드는 가능하다. 참고로 위에 보면 new Box< >(123)에 타입 매개변수를 안 집어넣었는데, 이런식으로 써도 컴파일러가 알아서 Integer로 넣어준다.

 

 제네릭 클래스의 타입 인자의 제한

 extends 키워드를 이용해서 제네릭 클래스가 받을 수 있는 인자를 제한 할 수 있다. 그리고 이렇게 제한을 했을 시에 따라오는 장점도 존재한다.

class AAA<T extends Number>{}  
//Number 클래스와 이를 상속받는 클래스만 T(인자)로 들어올 수 있다.

 원래 T에는 모든 인스턴스가 다 들어갈 수 있는데 위 처럼 extends Number를 하여 제한을 두면 Number 클래스 혹은 Number를 상속받는 클래스의 인스턴스들만 인자로 받을 수 있다.

 

 인자 제한의 장점

제한되지 않은 제네릭 클래스(Object 메소드만 호출)

 

Number 관련 클래스로 제한한 경우(Number 메소드도 호출가능)

 지금 보면 멤버 변수 ob가 호출할 수 있는 메소드의 차이가 보인다. Number로 제한된 경우에는 Number 클래스 관련 메소드를 호출할 수 있는데 반해, 제한되지 않은 경우에는 호출할 수 없다.

 

 이는 제네릭 멤버 변수는 타입이 명확히 정해져있지 않기 때문에 원래는 Object 클래스의 메소드만 호출 가능한 것이

원칙인데 반해,

지금처럼 특정 클래스 관련 타입인자로 제한이 된 경우에는 멤버 변수의 타입도 동일한 타입인자로 제한이 되기 때문에 해당 메소드를 호출하여도 아무런 문제가 되지 않기 때문에 관련 메소드를 호출 가능하다

 

 제네릭 클래스의 타입 인자를 인터페이스로 제한

 위에서는 클래스를 extends 하여 타입인자를 제한하였는데 이번에는 클래스 대신 인터페이스로 제한을 한다.

인터페이스도 클래스로 제한할 때와 마찬가지로 extends 키워드를 사용한다.

interface Eatable{
    public String eat();
}

class Apple implements Eatable{
    Apple(){
        System.out.println("I am apple");
    }
    @Override
    public String eat(){
        return "It tastes so good!";
    }
}
class Box<T extends Eatable>{  //인터페이스를 extends
    T ob;

    Box(T ob){
        this.ob = ob;
    }

    T get(){
        System.out.println(ob.eat()); //T가 Eatable로 제한되어 eat() 메소드 호출 가능
        return ob;
    }
}
public class BoundedInterfaceBox {
    public static void main(String[] args) {
        Box<Apple> appleBox = new Box<>(new Apple());

        Apple ap = appleBox.get();
    }
}


I am apple
It tastes so good!

 위에서 Box 클래스의 타입 인자를 인터페이스 Eatable로 extends(제한) 하고 있다. 여기서  T는  Eatable로 제한되었기 때문에 인터페이스의 기능을 보장한다. 따라서 ob.eat() 호출이 가능하다. 만일 Eatable을 extends하지 않았다면 클래스와 마찬가지로 Object의 메소드만 호출이 가능했을 것이다.

 

 마지막으로 타입인자를 제한할 때에는 하나의 클래스하나 이상의 인터페이스에 대해 동시에 제한할 수 있다.

class Box<T extends Number&Eatable&Sellable>{}
//클래스 Number, 2개의 인터페이스 Eatable, Sellable로 제한

'Java' 카테고리의 다른 글

Java- 제네릭(3) 와일드 카드  (0) 2023.01.27
Java- 제네릭(2)  (0) 2023.01.27
Java- Arrays 클래스  (0) 2023.01.06
Java- Random 클래스, 난수 생성  (0) 2023.01.06
Java- BigInteger, BigDecimal 클래스  (0) 2023.01.03