본문 바로가기
포스코x코딩온

[포스코x코딩온] 풀스택 부트캠프 18주차 정리 Java(Wrapper, generic)

by 김선지 2024. 2. 19.

Wrapper

원시값을 객체로 만들기 위해 존재하는 클래스라고 이해하면 편할 것 같다.

ArrayList나 Stream 메소드같은 경우도 (intStream을 이용하면 원시값의 stream이니까) 원시값이 들어갈 수 없기 때문에 boxed같은 함수를 통해서 boxing을 한번 해주고 넣어야 한다.

package _06_wrapper;

import java.util.stream.IntStream;

// Wrapper 객체: 기본 타입 값을 갖는 객체를 생성 가능
// - 포장 객체를 생성하기 위한 클래스는 'java.lang' 패키지에 포함되어 있음
// - 포장 객체는 포장하고 있는 기본 타입의 값을 변경할 수 없고, 단지 객체로 생성하는 데 목적이 있음
// - 컬렉션 객체 때문, ,기본 타입의 값은 저장할 수 없고, 객체만 저장 가능하기 때문
public class BoxingUnboxing {
    public static void main(String[] args) {
        // Boxing
        // - 기본 타입의 값을 Wrapper 객체로 만들어줌
        // - 포장 클래스 변수에 값이 대입될 때 발생
        Integer obj1 = 100;
        Double obj2 = 1.14;
        Character obj3 = 'A';
        System.out.println(obj1.intValue()); // intValue() : Integer 객체 내부의 int 값 리턴
        System.out.println(obj2.doubleValue());
        System.out.println(obj3.charValue());

        // Unboxing
        // - Wrapper 객체에서 원시 데이터를 얻는 과정
        // - 원시 타입 변수에 포장 객체가 대입될 때 생성
        int value1 = obj1;
        double value2 = obj2;
        char value3 = obj3;
        System.out.println(value1 + "   , " + value2 + "   , " + value3 );


        // 연산 시 boxing
        // - obj는 연산 시 unboxing 되어 연산됨
        int result1 = obj1 + 100;
        double result2 = obj2 + 1.5;
        char result3 = (char) (obj3 + 1);
        System.out.println(result1 + "   , " + result2 + "   , " + result3 );
    }
}

 


포장 클래스는 문자열을 기본 타입 값으로 변환할 때도 사용함
대부분의 포장 클래스에는 "parse + 기본타입" 명으로 된 static method가 존재 (오버로딩 없이 String만 인자에 들어갈 수 있다.)

ex) parseInt, parseDouble 등
:해당 메소드는 해당 기본타입 값으로 변환해줌

포장 값 비교
- 포장 객체는 내부 값을 비교하기 위해 ==, != 연산자 사용 불가능. equals 이용해야함
- why? 내부 값을 비교 하는 게 아니라 포장 객체의 주소값을 비교하기 때문

 - byte, short, int: -128 ~ 127
- boolean: true, false
- char: \u0000 ~ ~\u007f
위 범위에 포함되는 값은 이미 default로 만들어져 있음.
그래서 여러개 만들어도 포장 객체 공유 (같은 참조). 주소지가 같음.

이외의 값을 갖는다면 새로운 객체를 생성하고 그 주소값을 넣는다. (그렇게 된다면 new 연산자 대신 valueOf가 나을 것 같다.)

package _06_wrapper;

public class ValueCompare {
    public static void main(String[] args) {
        // -128 ~ 127
        Integer obj1 = 10;
        Integer obj2 = 11;
        Integer obj3 = 10;
        System.out.println("obj2 == obj1: " + (obj2 == obj1)); // false
        System.out.println("obj1 == obj3: " + (obj1 == obj3)); // true
        System.out.println("obj1 equals obj3: " + obj1.equals(obj3)); // true

        // 초과
        System.out.println();
        Integer obj4 = 1000;
        Integer obj5 = 1100;
        Integer obj6 = 1000;
        System.out.println("obj4 == obj5: " + (obj4 == obj5)); // false
        System.out.println("obj4 == obj6: " + (obj4 == obj6)); // false
        System.out.println("obj6 equals obj6: " + obj6.equals(obj6)); // true
    }
}

 


Generic

결정되지 않은 타입을 파라미터로 가진다고 가지는 클래스와 인터페이스

타입을 하나로 제약하지 않고 인스턴스를 만들 때 정의함으로써 타입이 여러가지가 들어올 수 있게 하는 것이라고 생각하면 될듯

 

Generic을 제한하려면

<T extends Number> 처럼 넣어주면 된다. 그러면 T는 Number를 extends한 것만 가능하기 때문에 byte, integer, double같은 class들만 들어가는 것이 가능해진다.

 

package _07_generic;

// 참고. Number class
// - Java에서 모든 숫자 형식의 부모 클래스
// - 정수형, 실수형 데이터타입 포함
// - Byte, Short, Integer 등등

// 제네릭 제한 Case1
// - Box 클래스는 제네릭 타입 T를 받지만 제한을 두어 Number를 상속한 클래스만 허용


class Box<T extends Number> {
    private T item;

    public T getItem() {
        return item;
    }

    public void setItem(T item) {
        this.item = item;
    }
}


// movable interface
interface Movable {
    void move();
}

class Car implements Movable {

    @Override
    public void move() {
        System.out.println("자동차 출발");
    }
}
// 제네릭 제한 타입 2
// Container 클래스는 제네릭 타입 T를 받지만 제한을 두어서 Movable interface를 구현한 클래스만 허용
// - makeItMove 메서드는 해당 객체의 move 메서드를 호출할 수 있음.
class Container<T extends Movable> {
    private T item;
    
    public Container(T item) {
        this.item = item;
    }
    
    public void makeItMove() {
        item.move();
    }
}


public class GenericRunner2 {
    public static void main(String[] args) {
    Box<Double> doubleBox = new Box<>();
       doubleBox.setItem(3.456);
        System.out.println(doubleBox.getItem());
    Container<Car> carContainer = new Container<>(new Car());
    carContainer.makeItMove();
    }



}

 

 

와일드카드

 

제네릭 타입을 매개값 or 리턴 타입으로 사용할  타입 파라미터로 ? 사용 가능하다.
제네릭에서 와일드카드(?) "  없는 타입" 의미함
? (Unbounded Wildcard): 어떠한 타입도   있다.

 

? extends T (Upper Bounded Wildcard): T 타입 또는 T 서브타입을 의미

=> ?는 T를 extends한 객체이므로 T 타입 본인이거나 T를 extends한 것만 들어갈 수 있다.


? super T (Lower Bounded Wildcard): T 타입 또는 T 슈퍼타입을 의미

=> ?는 T의 super 객체이기 때문에 T 타입 본인이거나 T의 super만 들어갈 수 있다.

public static void registerCourseA(Applicant<?> applicant) {
    System.out.println("kind: " + applicant.kind);
    System.out.println("kind.getClass(): " + applicant.kind.getClass());
    System.out.println(applicant.kind.getClass().getSimpleName() + "이 Course A를 등록함");
}

// Student 혹은 자식만 가능
public static void registerCourseB(Applicant<? extends Student> applicant) {
    System.out.println("kind: " + applicant.kind);
    System.out.println("kind.getClass(): " + applicant.kind.getClass());
    System.out.println(applicant.kind.getClass().getSimpleName() + "이 Course B를 등록함");
}
static void registerCourseC(Applicant<? super Teacher> applicant) {
    System.out.println("kind: " + applicant.kind);
    System.out.println("kind.getClass(): " + applicant.kind.getClass());
    System.out.println(applicant.kind.getClass().getSimpleName() + "이 Course C를 등록함");
}