Justin의 개발 로그
article thumbnail

5. 와일드카드 타입 <?>, <? extends ...> <? super ...>

제네릭 타입을 매개값이나 리턴 타입으로 사용할 때 구체적인 타입 대신에 와일드카드(?)를 사용할 수 있다.

  • <?> : 제한없음
  • <? extends 타입> : 지정된 타입 또는 하위 타입만 올 수 있다.
  • <? super 타입> : 지정된 타입 또는 상위 타입만 올 수 있다. 

 

5-1. 제네릭에서 와일드카드의 사용

public class Course<T> {
    private String name;
    private T[] students;
    private int index;
    private final int capacity;

    public Course(String name, int capacity) {
        this.name = name;
        this.capacity = capacity;
        this.students = (T[])(new Object[capacity]);
        this.index = 0;
    }

    public String getName() { return name;}
    public T[] getStudents() {return students;}

    public void add(T t) {
        Assert.isTrue(index < capacity, "Capacity를 초과했습니다.");

        students[index] = t;
        index++;
    }
}


public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() { return name; }

    @Override
    public String toString() {
        return name;
    }
}

public class Worker extends Person {

    public Worker(String name) {
        super(name);
    }
}

public class Student extends Person {
    public Student(String name) {
        super(name);
    }
}

public class HighStudent extends Student {
    public HighStudent(String name) {
        super(name);
    }
}

Person

|

|-----------

|                    |

Worker.   Student

                     |

                    HighStudent

 

 

class CourseTest {

    private static void registerCourse(Course<?> course) {
        return;
    }

    private static void registerCourseStudentByWildcards(
            Course<? extends Student> course
    ) {
        return;
    }

    @Test
    void 와일드카드테스트() {
        registerCourse(new Course<Person>("일반인 과정", 5));
        registerCourse(new Course<Student>("학생 과정", 5));
        registerCourse(new Course<Worker>("근로자 과정", 5));
        registerCourse(new Course<HighStudent>("고등학생 과정", 5));


        //registerCourseStudentByWildcards(new Course<Person>("일반인 과정", 5));   //캐스팅 에러 발생함
        registerCourseStudentByWildcards(new Course<Student>("학생 과정", 5));
        //registerCourseStudentByWildcards(new Course<Worker>("근로자 과정", 5));   //캐스팅 에러 발생함
        registerCourseStudentByWildcards(new Course<HighStudent>("고등학생 과정", 5));
    }
}

 

 

5-2. 함수형인터페이스를 활용할 때 ?(와일드카드) 사용

public class FunctionalProgram4Test {

    private static <T> Consumer<T> printAny() {
        return t -> System.out.println(t.toString());
    }

    private static Consumer<? super Number> printNumber() {
        return t -> System.out.println(t.toString());
    }

	// 함수형 인자로 Comsumer<T> 가능
    private static <T> void printAnyByFunction(Consumer<T> prt, T t) {
        prt.accept(t);
    }
	
    //제너릭 <T extends String>으로 선언 후 함수형 인자로 <? super T> 가능
    //왜냐? T exteds String 조건에 만족하는 인자는 모두 <? Super T> 조건에 부합하니까 가능함
    private static <T extends String> void printStringByFunction(Consumer<? super T> prt, T t) {
        prt.accept(t);
    }
    
    // <T extends String>를 <? extends T>로 변환은 불가능
    // String에게 3세대의 자식 클래스 계층이 있다고 할 때, 
	// 3세대를 2세대로 변환은 가능하지만, 
	// 2세대를 3세대로 변환은 불가능
    // (부모는 자식을 품을 수 있지만, 자식은 부모를 품지 못한다는 인생의 진리가 개발 언어에도 녹가 있는...)
    /* 
    private static <T extends String> void printStringByFunction(Consumer<? extends T> prt, T t) {
        prt.accept(t);
    }
    */



    @Test
    void test() {
        Consumer<String> prtAny = printAny();
        prtAny.accept("Hello world!!");

        var prtNumber = printNumber();
        prtNumber.accept(1);

        Stream<String> s = Arrays.asList("Test", "Abc", "Hello List").stream();
        s.forEach(prtAny);

        Stream<Long> sl = Arrays.asList(1L, 2L, 100L).stream();
        sl.forEach(prtNumber);

        printStringByFunction(printAny(), "이건 된다.");
        //printStringByFunction(printNumber(), "이건 안된다.");
        printAnyByFunction(printAny(), 1L);
        printAnyByFunction(printNumber(), 1L);
    }
    
    @Test
    void test2() {
        // 하나 더 해보자.
        Consumer<? super String> prtLambda = (t) -> System.out.println(t);
        prtLambda.accept( "Test" );
        printAnyByFunction(prtLambda, "Test");
    }

}

 

안되는 케이스 화면 캡처

profile

Justin의 개발 로그

@라이프노트

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!