Java

KH 교육과정 제네릭

최종군 2024. 7. 4. 19:33

 

제네릭 특징 :  

 

 

‘<>’ 안에 타입 파라미터가 위치

컬렉션,람다식(함수적 인터페이스), 스트림 NIO에서 널리 사용된다.

 

클래스, 인터페이스,메소드를 정의할 때 타입을 파라미터로 사용하여

컴파일 시 구체적인 타입이 결정될 수 있도록 도와준다 

 

제네릭 장점 : 

 

컴파일 시 강한 타입 체크를 할 수 있다.

 

타입 변환을 제거한다 

제네릭을 사용하지 않는 경우 불필요한 타입 변환이 필요하다 

 

제네릭 이전의 코드 : 

 

class Apple {

public String toString() { return "I am an apple."; }

}

 

class Orange {

public String toString() { return "I am an orange."; }

}

 

// 다음 상자는 사과도 오렌지도 담을 수 있다.

class Box {

private Object ob;   // 최상위 클래스 object 타입의 ob 필드 

 

public void set(Object o) {

ob = o;

}

 

 

public Object get() {

return ob;

 

}

 

}

 

 

제네릭 이전의 코드의 사용의 예 :

 

 

public static void main(String[] args) {

 

Box aBox = new Box(); // 상자 생성

Box oBox = new Box(); // 상자 생성

 

aBox.set(new Apple()); // 상자에 사과를 담는다.

oBox.set(new Orange()); // 상자에 오렌지를 담는다.

 

 

Apple ap = (Apple)aBox.get();

Orange og = (Orange)oBox.get();

 

형변환 과정이 수반된다. 

 

* 업캐스팅(UpCasting ) -- > 자식 클래스가 부모 클래스 타입으로 캐스팅 되는 것

업캐스팅은 캐스팅 연산자 괄호()를 생략할 수 있다 

업캐스팅을 하고 메소드를 실행을 할 경우 클래스에서 오버라이딩한 메서드가 있을 경우

부모 클래스의 메서드가 아닌 오버라이딩 된 메서드가 실행이 된다 

 

*다운 캐스팅(DownCasting) --->

거꾸로 부모클래스가 자식 클래스 타입으로 캐스팅되는 것

다운캐스팅의 목적은 업캐스팅한 객체를 다시 자식 클래스 타입의 객체로 되돌리는데

목적을 둔다 업캐스팅 되지 않는 부모 객체일 경우 

그대로 다운캐스팅을 하면 오류가 발생한다 

 

다운캐스팅에서는 에디터에서 컴파일 에러가 발생하지 않고 런타임 에러가 발생하는 위험성이 있다 

같은 부모 클래스를 상속하고 있더라도 형제 클래스 끼리는 서로 캐스팅이 불가능하다  

 

 

System.out.println(ap);

System.out.println(og);

 

 

public static void main(String[] args) {


Box aBox = new Box();

Box oBox = new Box();

 

// 아래 두 문장에서는 사과와 오렌지가 아닌 문자열을 담는 경우 

aBox.set("Apple");

oBox.set("Orange");

 

// 상자에 과일이 담기지 않았는데 과일을 꺼내려 한다.

 

Apple ap = (Apple)aBox.get();

Orange og = (Orange)oBox.get();

 

System.out.println(ap);

System.out.println(og);

 

런타임 에러가 발생한다

ClassCastException

 

 

 

제네릭 이전 코드가 갖는 문제점 2 :

 

public static void main(String[] args) {

 

Box aBox = new Box();

Box oBox = new Box();

 

aBox.set("Apple");

oBox.set("Orange");

 

System.out.println(aBox.get());

System.out.println(oBox.get());

}

 

 

제네릭으로 클래스 정의하기 

 

class Box {

private T ob;

 

public void set(T o) {

ob = o;

}

 

public T get() { return ob;

         

    }

}

 

 

 

• 타입 매개변수 (Type Parameter)     Box<T>에서  T

 

• 타입 인자 (Type Argument) Box<Apple>에서 Apple

 

• 매개변수화 타입 (Parameterized Type) Box<Apple>

 

 


 

 

class Box <T>{

private T ob;

public void set(T o) {

ob = o;

}

 

 

public T get() {  // 메소드 

return ob;

   }

}

 

Box <Apple> aBox = new Box<Apple>()

 

- > T를 Apple로 결정하여 인스턴스 생성 

-> 따라서 Apple 또는 Apple을 상속하는 하위 클래스의 인스턴스 저장가능하다 

 

 

 

public static void main(String[] args) {

Box aBox = new Box();

Box oBox = new Box();

 

aBox.set("Apple");

oBox.set("Orange");

 

Apple ap = aBox.get();

Orange og = oBox.get();

 

System.out.println(ap);

System.out.println(og);

 

-- > 기존 제네릭을 사용하지 않은 경우

런타임 오류가 발생했더라면 

 

제네릭을 통해서 컴파일 오류로 이어지게된다 

 

제네릭은 기본 자료형을 사용할 수 없다 

래퍼 클래스를 통해서 사용이 가능하다 

 

class PrimitivesAndGeneric {

public static void main(String[] args) {

Box <Integer>iBox = new Box<>(); -- > <> : 생략이 가능하다 

 

iBox.set(125); / / *오토 박싱이 진행된다. 

기본 객체를 Wrapper로 바꿔주는 것을 박싱이라고 한다. 

 

int num = iBox.get(); // *오토 언박싱이 진행된다 

Wrapper 클래스 타입의 값을 기본 자료형으로 바꿔주는 걸 언박싱이라고한다 

 

 


 

 

 

// 정수형 데이터를 다루는 클래스 : DataClass
class DataClss<T>{

    private T data; // 필드의 타입(자료형)

    public  T getData(){
        return data;
    } // 메소드 반환타입 T
    public void setData(T data){
        this.data = data;
    }// 메소드의 매개변수 타입 T
}
public class GenericClassRun {
    public static void main(String[] args) {

        // 제네릭 적용 안 했을 때 상황
        //정수형, 실수형 모두 관리하고자 할 떄 ==> 실수로 인해

        DataClss<Double> d1 = new DataClss<>();

        d1.setData(10.0);
        System.out.println(d1.getData());

        DataClss<Integer> d2 = new DataClss<>();
        d2.setData(1000);
        System.out.println(d2.getData());

        DataClss<String> d3 = new DataClss<>();
        d3.setData("Apple");
        System.out.println(d3.getData());

 

 

인터페이스는 추상 메소드를 갖는다 

 

class에 인터페이스를 구현(implements) 시

접근 제한자를 설정해야한다 오버라이딩 조건 중 

접근제한자가 범위가 크거나 같아야한다. 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

package com.kh.practice;

public class GenericExtendsRun <T extends Number> {
    public static void main(String[] args) {


        print(12312313);
        print(3.1415926535897);

        // print("wow");

        // String 클래스는 Number 클래스를 상속하지 않음

    }
    // 제네릭 메소드 정의 : Number 클래스의 상속받은 클래스만 타입으로 받는 메소드
    // 전달된 값을 콘솔창에 출력하는 기능 (print)

    public static <T extends Number> void print(T value){
        System.out.println(value);
    }


}

 

 

 

package com.kh.practice;



class DataCalss02{

    // 제네릭 메소드
    public <T> void test(Integer num){
        T data; // 메소드 지역변수에 제네릭 적용(사용)
        // 형변환도 가능
        data = (T)num; // 형변환도 가능!
    }
    public <T> T test2(T data){
        // 제네릭을 반환타입, 매개변수 타입에 적용
        T t;
        t = data;
        return t; }}
class  DataClass02_1<T>{
    public <K> void test(T data, K data2){
    }}
public class GenericMethod {
    public static void main(String[] args) {

        DataCalss02 d1 = new DataCalss02();
        d1.test(123456); // 타입을 지정하지 않으면, T(타입매개변수)--->Object

        d1.<Double>test(777); // T ---> Double
        d1.test2("Orange"); // T를 지정하지 않으면 제네릭타입 자동으로 결정 T ---> String
        d1.<Integer>test2(999999999); // T ---> Integer 지정을 한 경우

        DataClass02_1 <String> d2 = new DataClass02_1<>();
        d2.<Integer>test("test1",100);
        d2.<Double>test("test2",3.14);
        d2.<String>test("test3","zzzz");

        //  메소드 자리
        // 첫번째 매개변수타입은 객체 생성 시점에 정해짐
        // 두번째 매개변수 타입은 메소드 호출 시점에 정해짐
    }

 

 

 

package com.kh.practice;


class DataClass03<L,R>{
    private L left;
    private R right;

    public L getLeft(){
        return left;
    }
    public void setLeft(L left){
        this.left = left;
    }
    public R getRight(){
        return right;
    }
    public  void setRight(R right){
        this.right = right;
    }
}
public class MultiGeneric {
    public static void main(String[] args) {

        DataClass03<String,Integer> d03 = new DataClass03<>();

        d03.setLeft("레프트");
        d03.setRight(100);

        String letf = d03.getLeft(); // 언박싱 

        int right = d03.getRight(); // 언박싱 

        System.out.println(d03.getLeft());
        System.out.println(d03.getRight());

        DataClass03<Double,Boolean> d2 = new DataClass03<>();

        d2.setLeft(111.22);
        d2.setRight(false);

        System.out.println(d2.getLeft());
        System.out.println(d2.getRight());