(미완성) JAVA Generic 제네릭(2)/한정형 파라미터, 와일드카드형, 제네릭형의 상속과 실장

  • by

제한된 유형 매개 변수 ()

유형 매개변수에 지정된 특정 유형을 제한해야 하는 경우가 많습니다.

숫자를 계산하는 제네릭 메서드는 Number 유형 또는 하위 클래스 유형(Byte, Short, Integer, Long, Double, Float)의 인스턴스만 매개 변수로 가져와야 합니다.

이것이 제한된 유형 매개 변수가 필요한 이유입니다.

제한된 유형 매개변수를 선언하려면 유형 매개변수 뒤에 extends 키워드를 사용하여 상위 유형을 지정하십시오.

상위 타입은 클래스뿐만 아니라 인터페이스도 가능하다.

인터페이스로 implements를 사용하지 마십시오.

public <T extends 상위타입> 리턴타입 메소드(매개변수, ...) {...}

형태 파라미터에 포함되는 특정의 형태는, 부모형이거나, 부모형의 아이 또는 구현 클래스만이 가능합니다.

주의해야 할 점은, 메소드의 중괄호{}내에서 형태 파라미터 변수로서 사용 가능한 것은, 상위형의 멤버(필드, 메소드)에 제한된다.

하위 유형에만 있는 필드와 메서드는 사용할 수 없습니다.

다음은, 수치형만을 구체적인 형태로 가지는 제네릭 메소드 compare() 입니다.

2개의 수치형을 파라미터로서 받아, 차를 돌려줍니다.

public <T extends Number> int compare(T t1, T t2) {
    double v1 = t1.doubleValue(); // Number의 doubleValue() 메소드 사용
    double v2 = t2.doubleValue(); // Number의 doubleValue() 메소드 사용
    return Double.comapre(v1, v2);
}

doubleValue() 메서드는 Number 클래스에 정의된 메서드로 숫자를 double 형식으로 변환합니다.

Double.comapre() 메서드는 첫 번째 매개 변수가 작으면 -1을 반환하고, 같으면 0을 반환하고, 큰 경우 1을 반환합니다.

(Util.java) 제네릭 메소드 compare () 정의

public class Util {
    public static <T extends Number> int compare(T t1, T t2) {
        double v1 = t1.doubleValue();
        double v2 = t2.doubleValue();
        return Double.compare(v1, v2);
    }
}

(BoundedTypeParameterExample.java) 제네릭 메소드 compare () 호출

public class example {
    public static void main(String() args) {
//        String str = Util.compare("a", "b"); // String은 Number 타입이 아니므로 오류발생.
        int result1 = Util.compare(10, 20); //int 20 -> Integer AutoBoxing
        System.out.println("result1 = " + result1);

        int result2 = Util.compare(4.5, 3); //double 4.5 -> Double AutoBoxing
        System.out.println("result1 = " + result2);
    }
}

result1의 인수 10과 20, result2의 두 번째 인수 3은 제네릭 형식 T를 상속한 최상위 형식의 Number에 의해 하위 클래스인 Integer Wrapper 클래스에 AutoBoxing됩니다.

result2의 첫 번째 재인수 값 4.5는 Number의 서브클래스 인스턴스인 Double Wrapper 클래스 유형으로 AutoBoxing됩니다.

와일드 카드 타입 (, , )

코드 내에서? 를 일반적으로 와일드카드(wildcard)라고 합니다.

제네릭 타입을 파라미터값 또는 리턴 타입으로 사용하는 경우, 구체적인 타입 대신에 와일드카드를 다음과 같이 3개의 형식으로 사용할 수 있다.

· 제네릭 타입 :Unbounded Wildcards(제한 없음)

형태 파라미터를 옮겨놓는 구체적인 형태로, 모든 클래스나 인터페이스형이 올 수 있다.

· 제네릭 타입<? extends トップタイプ> :Upper Bounded Wildcards(부모 클래스 제한)

타입 파라미터를 치환하는 구체적인 타입으로, 상위 타입이나 하위 타입만이 올 수 있다.

· 제네릭 타입<? super サブタイプ> :Lower Bounded Wildcards(서브 클래스 제한)

타입 파라미터를 치환하는 구체적인 타입으로, 서브 타입이나 상위 타입이 올 수 있다.

(Course.java) 제네릭 타입

class Course<T> {
    private String name;
    private T() students;

    public Course(String name, int capacity) {
        this.name = name;
//        this.student = student;
        this.students = (T()) (new Object(capacity));
    }

    public String getName() {
        return name;
    }

    public T() getStudent() {
        return students;
    }

    public void add(T t) {
        for (int i = 0; i < students.length; i++) {
            if (students(i) == null) {
                students(i) = t;
                break;
            }
        }
    }
}

제네릭 타입 코스는 코스 클래스에서 코스명과 수강생을 보존할 수 있는 배열을 가지고 있다.

타입 파라미터 T가 적용되는 위치는 수강생 타입 부분이다.

수강생이 될 수 있는 타입은 다음과 같다.


Person의 서브 클래스로서 Worker와 Student가 있고, Student의 서브 클래스로서 HighStudent가있다.

· Course

학생은 모든 유형(Person, Worker, Student, HighStudent)이 됩니다.

· Course<? extends Student>

수강생은 Student와 HighStudent만 할 수 있습니다.

· Course<? super Worker>

수강생은 Worker와 Person만이 됩니다.

public class WildCardExample {
    public static void registerCourse(Course<?> course) { //모든 과정
        System.out.println(course.getName() + "수강생 : " + Arrays.toString(course.getStudents()));
    }
    public static void registerCourseStudent(Course<? extends Student> course) { // 학생 과정
        //System.out.println(course.getName() + "수강생 : " + Arrays.toString(course.getStudents()));
        System.out.println(course.getName() + "수강생 : " + Arrays.toString(new Student()(){course.getStudents()}));

    }
    public static void registerCourseWorker(Course<? super Worker> course) { // 직장인과 일반인 과정
        System.out.println(course.getName() + "수강생 : " + Arrays.toString(course.getStudents()));
    }
    public static void main(String() args) {
        /* 일반인 과정 생성 */
        Course<Person> personCourse = new Course<Person>("일반인 과정", 5);
        personCourse.add(new Person("일반인"));
        personCourse.add(new Worker("직장인"));
        personCourse.add(new Student("학생"));
        /* 직장인 과정 생성 */
        Course<Worker> workerCourse = new Course<Worker>("직장인 과정", 5);
        workerCourse.add(new Worker("일반인"));
        /* 학생 과정 생성 */
        Course<Student> studentCourse = new Course<Student>("학생 과정", 5);
        studentCourse.add(new Student("학생"));
        studentCourse.add(new HighStudent("고등학생"));
        /* 고등학생 과정 생성 */
        Course<HighStudent> highStudentCourse = new Course<HighStudent>("고등학생 과정", 5);
        highStudentCourse.add(new HighStudent("고등학생"));
    }
    /* 모든 과정 등록 가능 */
    registerCourse(personCourse);
    registerCourse(workerCourse);
    registerCourse(studentCourse);
    registerCourse(highStudentCourse);
    /* 학생 과정만 등록 가능 */
    registerCourseStudent(studentCourse);
    registerCourseStudent(highStudentCourse);
    /* 직장인과 일반인 과정만 등록 가능 */
    registerCourseWorker(personCourse);
    registerCourseWorker(workerCourse);
}

registerCourseXXX() 메서드의 매개 변수로 와일드카드 유형을 사용했습니다.

registerCourse() : 모든 수강생이 들을 수 있는 코스 등록

registerCourseStudent() : 학생만 들을 수 있는 코스 등록

registerCourseWorker() : 회사원만 들을 수 있는 코스 등록


책을 찾아도 Person, Worker, Student, HighStudent class는 없는데 카페에 있는지 전인지 책이 두껍고 재확인할 수 없었다.

수업은 쉽게 만들면 좋지만 초보자에게는 부끄러운 부분

초급 대상이지만 generic 이야기하면서 covariance/contravariance라는 것이 있을 정도는 말해주어야 하는 것 같지만… 이 부분은 무엇이 맞는지 잘 모른다.

그러나 용어로도 모르면 나중에 찾을 수 없을까?

https://www.hanbit.co.kr/media/community/review_view.html?hbr_idx=6012

이것이 자바

‘결론은 ‘Java’다.

가장 중요한 프로그래밍 언어를 배워야 할 경우,

www.hanbit.co.kr


제네릭 형식의 상속 및 구현

제네릭 형식도 다른 형식과 마찬가지로 부모 클래스가 될 수 있습니다.

class MemberDAOImpl {
    public static void main(String() args) {

        MemberVO memberVO = null;
        List<MemberVO> mList = memberList(memberVO);
    }
    public static <T extends objectVO> List<T> memberList(T t) {
        List<T> mList = new ArrayList<>();
        T voObject = (T) new Object(); // Type parameter 'T' cannot be instantiated directly - 제네릭의 타입 파라미터는 인스턴스 생성 불가능
        voObject.setExValue(3);
        int exValue = voObject.getExValue();
        System.out.println(exValue); // 3 출력
        mList.add
        return mList;
    }
}
abstract class objectVO {
    public int exValue;
    public abstract int getExValue();
    public abstract void setExValue(int exValue);
}
class MemberVO extends objectVO{
    private int exValue;

    @Override
    public int getExValue() {
        return exValue;
    }

    @Override
    public void setExValue(int exValue) {
        this.exValue = exValue;
    }
}