Java

[Java] 12. 제네릭 프로그래밍

hyomee2 2024. 8. 21. 19:40

: 데이터 형식에 의존없이 여러 다른 데이터 타입을 가질 수 있는 기술로 재사용성을 높일 수 있다.

- 하나의 클래스만 작성해도 여러 타입의 필드 값을 가진 클래스로 변형해서 다룰 수 있는 구현의 편리함이 있다.

- 컴파일 시 미리 타입 검사를 하여 객체의 타입 안정성을 높이고, 반환 값에 대한 타입 변환 및 타입 검사에 들어가는 코드도 생략할 수 있다.

public class GenericTest<T> {

	private T value;
	
	public T getValue() {
		return value;
	}
	
	public void setValue(T value) {
		this.value = value;
	}
}
public class Application {
    public static void main(String[] args) {
    
        /* 타입을 Integer로 하여 인스턴스 생성 */
        GenericTest<Integer> gt1 = new GenericTest<>();
        gt1.setValue(10);
        // gt1.setValue("Hello World!");
        Integer num = gt1.getValue();

        /* 타입을 String으로 하여 인스턴스 생성 */
        GenericTest<String> gt2 = new GenericTest<>();
        gt2.setValue("Hello");
        // gt2.setValue(10);
        String str = gt2.getValue();

        /* 제네릭 없이 인스턴스 생성 */
        GenericTest gt3 = new GenericTest();
        gt3.setValue("Hello");
        gt3.setValue(10);
        Integer num2 = (Integer) gt3.getValue();
    }
}

 


 

와일드 카드

: 제네릭 클래스의 인스턴스를 유연하게 활용하기 위한 문법으로, 메소드의 매개변수로 받을 시 타입을 원하는 만큼으로 제한하여 불특정한 제네릭 클래스 타입을 조금 더 활용할 수 있다.

- <?>: 모든 타입을 허용하는 와일드 카드

- <? extends T>: T 타입 또는 T의 하위 타입을 허용하는 와일드 카드

- <? super T>: T 타입 또는 T의 상위 타입을 허용하는 와일드 카드

public interface Animal {
}


/* 포유류도 동물이므로 Animal 인터페이스 구현 */
public class Mammal implements Animal {
}

/* 토끼는 포유류이다. */
public class Rabbit extends Mammal {
    public void cry() {
        System.out.println("토끼가 울음 소리를 냅니다 끼끾");
    }
}

/* 바니도 토끼이다. */
public class Bunny extends Rabbit {
    @Override
    public void cry() {
        System.out.println("바니바니 바니바니 당근 당근");
    }
}

/* 드렁큰 바니도 바니이다. */
public class DrunkenBunny extends Bunny {
    @Override
    public void cry() {
        System.out.println("봐니봐니 봐니봐니 당근 당근@!#$@%");
    }
}


/* 파충류도 동물이므로 Animal 을 구현 */
public class Reptile implements Animal {
}

/* 뱀은 파충류이다. */
public class Snake extends Reptile {
}
public class RabbitFarm <T extends Rabbit> {

    private T rabbit;

    public RabbitFarm() {}

    public RabbitFarm(T rabbit) {
        this.rabbit = rabbit;
    }

    public T getRabbit() {
        return rabbit;
    }

    public void setRabbit(T rabbit) {
        this.rabbit = rabbit;
    }
}


public class Application1 {
    public static void main(String[] args) {
        /* extends 키워드를 이용하여 특정 타입만 사용하도록 제네릭 범위를 제한할 수 있다. */

        /* Rabbit의 상위 타입이거나 관련 없는 타입으로 인스턴스 생성하는 것은 불가능*/
        // RabbitFarm<Animal> farm1 = new RabbitFarm<>();
        // RabbitFarm<Mammal> farm2 = new RabbitFarm<>();
        // RabbitFarm<Snake> farm3 = new RabbitFarm<>();

        /* Rabbit 타입이거나 Rabbit의 후손 타입으로 인스턴스 생성이 가능하다. */
        RabbitFarm<Rabbit> farm1 = new RabbitFarm<>();
        RabbitFarm<Bunny> farm2 = new RabbitFarm<>();
        RabbitFarm<DrunkenBunny> farm3 = new RabbitFarm<>();

        farm1.setRabbit(new Rabbit());
        farm1.getRabbit().cry();

        farm2.setRabbit(new Bunny());
        farm2.getRabbit().cry();

        farm3.setRabbit(new DrunkenBunny());
        farm3.getRabbit().cry();
    }
}
public class MethodFarm {
    /* 메소드 매개변수로 전달 받는 타입 제한 */
    public <T> void animalType(T t1, T t2) {}
    public <T extends Mammal> void mammalType(T t) {}
    public <T extends Reptile> void reptileType(T t) {}
}

public class Application2 {
    public static void main(String[] args) {
        /* 메소드 매개변수 제네릭 제약을 사용할 수 있다. */
        MethodFarm methodFarm = new MethodFarm();

        /* 별도의 제약 없음 */
        methodFarm.animalType(new Bunny(), new Snake());

        /* extends Mammal */
        // methodFarm.mammalType(new Snake());
        methodFarm.mammalType(new Bunny());

        /* extends Reptile */
        methodFarm.reptileType(new Snake());
        // methodFarm.reptileType(new Bunny());
    }
}
public class WildCardFarm {
    public void anyType(RabbitFarm<?> farm) {
        farm.getRabbit().cry();
    }

    public void extendType(RabbitFarm<? extends Bunny> farm) {
        farm.getRabbit().cry();
    }

    public void superType(RabbitFarm<? super Bunny> farm) {
        farm.getRabbit().cry();
    }
}

public class Application3 {
    public static void main(String[] args) {
    
        WildCardFarm wf = new WildCardFarm();

        /* 1. 매개변수 타입 제한이 없는 경우 => 어떤 토끼를 가진 토끼 농장이던 인자로 전달 가능 */
        wf.anyType(new RabbitFarm<>(new Rabbit()));
        wf.anyType(new RabbitFarm<>(new Bunny()));
        wf.anyType(new RabbitFarm<>(new DrunkenBunny()));

        /* 2. 매개변수 타입이 바니이거나 바니 후손 토끼를 가진 토끼 농장만 인자로 전달 가능 */
        // wf.extendType(new RabbitFarm<>(new Rabbit()));
        wf.extendType(new RabbitFarm<>(new Bunny()));
        wf.extendType(new RabbitFarm<>(new DrunkenBunny()));

        /* 3. 매개변수 타입이 바니이거나 바니 상위 타입 토끼를 가진 토끼 농장만 인자로 전달 가능 */
        wf.superType(new RabbitFarm<>(new Rabbit()));
        wf.superType(new RabbitFarm<>(new Bunny()));
        // wf.superType(new RabbitFarm<DrunkenBunny>(new DrunkenBunny()));

        /* RabbitFarm<DrunkenBunny> drunkenBunnyRabbitFarm = new RabbitFarm<>(new DrunkenBunny());
        wf.superType(drunkenBunnyRabbitFarm);
        RabbitFarm<Bunny> bunnyRabbitFarm = new RabbitFarm<>(new DrunkenBunny());
        wf.superType(bunnyRabbitFarm);*/

    }
}

'Java' 카테고리의 다른 글

[Java] 13. Collection (1) List - ArrayList  (0) 2024.08.22
[Java] 13. Collection  (0) 2024.08.22
[Java] 11. 입출력  (0) 2024.08.21
[Java] 10. 예외처리 (1)  (0) 2024.08.21
[Java] 배열의 값을 출력하기(반복문, Arrays.toString())  (0) 2024.08.11