Java_제네릭

ChoRong0824·2023년 5월 13일
0

Java

목록 보기
17/31
post-thumbnail

자바 제네릭이 필요한 이유, 사용법, 개념을 문제위주로 정리했습니다.

제네릭의 개념과 필요성 (코드로 설명)

모든 타입을 다 받는 클래스 만들기

______ 에 어떤 것이 들어와야 하는지 작성
hint, 이 문제는 자바 문법중 제네릭에 대한 개념과 필요성에 대해서 알고 있는지를 묻는 문제입니다.
class Sample {
	private _________ a;
	Sample( __________ x ) {
	this.a = x;
}
	public __________ getA() {
		return a; 
	}
}
main 함수
public class collection_Generic1 { 
	public static void main(String[] args) {
    
	Sample s1 = new Sample("안녕하세요~");
    System.out.println( s1.getA() );
	Sample s2 = new Sample(100); 
    System.out.println( s2.getA() );

정답
class Sample{
    //Field
    private Object obj;

    //Constructor
    Sample(Object x){
        this.obj= x;
    }

    //Method
    public Object getObj(){
        return obj;
    }
    //정보 출력
    void printInfo(){
        System.out.println(obj.getClass().getName()); // 객체가 속하는 클래스의 정보를 출력하는 메서드
    }
}

public class Generic1 {
    public static void main(String[] args) {

        // [1] : 객체 생성 --> 문자열
        Sample s1 = new Sample("안녕하세요");
        System.out.println(s1.getObj());
        s1.printInfo();
		
        System.out.println("------------------------------------------");
        
        // [2] : 객체 생성 -->  숫자
        Sample s2 = new Sample(100); // ERR (클래스의 타입이 int, String 일 경우) -->
        // 타입을 Object 로 바꿔주면 해결됨. --> Object 타입으로 지정시, 어떤 객체를 생성하고 불러도 다 가능함
        System.out.println(s2.getObj());
        s2.printInfo();

		System.out.println("------------------------------------------");
        
    }
}

Object 코드

		// [3] : 객체 생성 --> Object
        Sample s3 = new Sample(new Object()); // 오브젝트 객체를 생성해서 샘플 객체에 보내는 것이 됨
        // --> (로직)1. 생성자에서 받아서 호출됨, 타입이 오브젝트이니까 모든 타입을 받아줌. --> 오브젝트로 넘겨주니까 오브젝트로 받는 것은 당연.
        System.out.println(s3.getObj()); //객체니까 객체 주소값 출력함
        s3.printInfo(); // 객체의 정보 출력됨

        // [4] : 위와같이 사용시 --> 단점 존재
        // s1 --> 문자열
        s1.getObj(); // 이 메서드를 호출하면, (메서드 안에서) return 해주니까 뭔가를 반환하는 중임. --> obj 에 있는 값을 그대로 리턴해줌
        // --> 메서드 타입은 오브젝트 --> (이말은) 리턴되는 반환타입이 오브젝트임.
        // --> (Constructor)생성자가 받을 때엔 좋았는데, 이것을 통해서 (Method)에서 내보낼 땐 반환타입이 오브젝트라서, 상관이 생김.
        // --> 그때그때 사용할 때마다, 형변환을 해주어야합니다. string 사용시 (string)으로

		Object str = s1.getObj(); // 리턴시 반환 타입이 Object입니다.
        System.out.println(str); // 정상코드,("안녕하세요")
        System.out.print(str.length()); // ERR, (문자열의 길이를 알아보려니까)
        
        String str = (String)s1.getObj; //이렇게 형변환으로 사용해야 하는 이유가,
        // 문자열의 길이 및  String 클래스가 가진 유용한 여러 메서드를 사용하기 위해서 형변환함.
        System.out.print(str.length()); // 6, 정상작동 코드


        /*
        // case 1, (문자)
        String str = s1.getObj(); // ERR . 리턴시 반환 타입이 Object 임.
        //-- > 즉, Object 로 반환되다 보니까, ERR 나지 않게 하려면, 형변환을 통해서 사용해야합니다.
        String str = (String)s1.getObj(); // 정상작동하는 코드
        
        // case 2, 숫자(정수)
        int num = s2.getObj(); //ERR
        int num = (int)s2.getObj(); // 됨. -- > 사용할 때마다 형변환을 해줘야함. --> 번거로움
        */
        
        // 둘 중 하나로 수정
        // 1) int num = (int)s2.getObj();
        Object num = s2.getObj();
        // s2 --> 숫자
        System.out.print(num+100); // 이렇게 하게 된다면 ? --> ERR. 
        // --> 오브젝트기 때문에, 직접적인 연산 안됨. -- > 형변환 해줘야함
        System.out.print((int)num+100); // 이렇게 형변환해줘야함
        
        int num = (int)s2.getObj(); // 이렇게 형변환 해주면 무상관.
        System.out.print(num+100); // 200, 계산가능해짐. 위에 형변환해줘서

Awesome !

이러한 단점들을 보완하기 위해서 Generic 이 나오게 되었습니다.


ClassCast ERR

컴파일 단계에서는 에러가 안나고, 실행 단계에서 ClassCast 오류가 발생하는 상황 만들기

hint, 제네릭의 필요성을 보여주는 예를 코드로 설명할 수 있는지 확인하는 문제입니다.
code
class Person{
	public Object obj;
    Person( Object obj) {
    	this,obj = obj;
	}
}
class Student{
	public int grade;
    Student(int grade) {
    	this.grade = grade;
	}
}
public class GenericTest{
	public void main(String[]args){
    	----------------------------------------
        ----------------------------------------
    }
}

제네릭 필요성

필자는, (컴파일 되고)잘 수행되지만 에러가 발생하지 않는경우 상당히 안좋은 코드입니다.
따라서, 컴파일러 단계에서 에러가 발생해야 상당히 좋은 코드라고 생각합니다.
--> 제네릭이 필요한 이유

정답코드

class Person {
    //Field
    public Object obj;

    //Constructor
    Person (Student obj){
        this.obj = obj; // 파라미터로 넘어오는 obj 를 obj 로 할당
    }
}
class Student{
    //Field
    public int grade;

    //Constructor
    Student(int grade){
        this.grade = grade;
    }
}
class Teacher{

}
public class Generic2 {
    public static void main(String[] args) {

        // [1] : 객체생성
        Person p1 = new Person(new Student(3)); // 참조형 객체 생성 --> Person 생성자에 어떤 값을
        // 보내주더라도 object 라서 다 받아줄 수 있음
        System.out.println(p1.obj);
        // Person p2 = new Person(new Teacher()); // ERR.--> Person 생성자의
        // 매개변수의 타입을 Student 로 지정해서, 학생 객체만 받을 수 있게 해둬서
        // 매개변수 타입이 Object 라면, Err 해결할 수 있음.

        /*
        [2] : 객체생성
        Person p3 = new Person(new Student(1));
        Student s1 = (Student) p1.obj; // 이렇게 형변환을 해서 사용해야함. 아니면 ERR.
        String str = (String) p1.obj;
        즉, 사용할 때엔 그에 맞는 형변환이 필요함.
        System.out.println(str.length()); // 6
         */
         
		// [3] : 형변환 ->cast
        Teacher t1 = (Teacher)p1.obj; // 이 부분은 컴파일 단게에서는 에러가 안나고,
        // 컴파일이 잘됨. --> 실행하는 단계에서 ClassCast 오류가 발생합니다.
    }
}

제네릭 개념과 사용법을 예제 코드로 구현

아래의 빈칸을 제네렉으로 구성해보세요
hint, 제네릭에 대한 개념과 사용법
// [1] : 객체 생성 --> String
Sample_______________ s1 = new Sample _______________( "안녕" );


// [2] : 객체 생성 --> Integer
Sample _______________ s2 = new Sample _______________(10);
  • Generic : 포괄적인, 총칭하는, 일반적인

Code

import java.lang.*;
class Sample<T> { // 이게 키포인트. Integer, String 등, 어떤 타입으로 올 지 모르니까 포괄적인 T 타입(제네릭)을 사용해줌.
    //Field
    private T obj; // 아무 문자나 쓰는데, 대문자 'T' (Type)를 씀
    //Constructor
    Sample(T x) { //포괄적인 타입을 받아주겠다는 소리
        this.obj= x;
    }
    //Method
    T getObj(){ // 제네릭 타입의 T 로 반환,
        return obj;
    }
    void printInfo(){
        System.out.println(obj.getClass().getName());
    }
}
public class Generic3 {
    public static void main(String[] args) {
        // [1] : 객체 생성 --> String
        Sample<String> s1 = new Sample<String>( "안녕하세요~" ); // <타입명>, 타입명은 다양한 타입으로 바뀔 수 있음. -->
        // 즉, 다양한 제네릭 타입으로 바뀔 수 있음. --> 즉, 받는 쪽에서, 샘플클래스를 통해 객체를 만들 떄,
        // 다양한 타입이 만들(넘어올) 수 있겠구나를 생각해내야함. --> 다양한 타입으로 객체생성 될 수 있음 -->클래스 옆에 <T>붙임
        // <결론> 다양한 타입이 적혀질 수 있음(메인부분에)
        // --> (샘플클래스 부분에) 다양한 타입을 받을 수 있게 `<T>`, (필드,생성자,메서드)의 타입도 'T'
        System.out.println(s1.getObj());
        s1.printInfo();
        
        System.out.println("------------------------------------------------");
        // [2] 객체생성 --> Integer
        Sample<Integer> s2 = new Sample<Integer>(100);
        System.out.println(s2.getObj());
        s2.printInfo();
        
        System.out.println("------------------------------------------------");
         // [3] 형변환 없이 사용해보기
        String str = s1.getObj(); // 객체를 생성할 때, String 타입으로 되어있어서 가능함.
        // --> 앞에서는 Object 타입이라서 `String str =(String)s1.getObj();` 을 해줬어야함. BUT, 지금은 형변환 필요x
        System.out.println(str.length()); // 6
        
        // 즉, 제네릭을 사용하면 형변환을 고민할 필요없음. 형변환 없이 바로 사용가능.
        System.out.println(s1.getObj().length()); // 6
        System.out.print(s2.getObj()+100); // 200
    }
}
profile
컴퓨터공학과에 재학중이며, 백엔드를 지향하고 있습니다. 많이 부족하지만 열심히 노력해서 실력을 갈고 닦겠습니다. 부족하고 틀린 부분이 있을 수도 있지만 이쁘게 봐주시면 감사하겠습니다. 틀린 부분은 댓글 남겨주시면 제가 따로 학습 및 자료를 찾아봐서 제 것으로 만들도록 하겠습니다. 귀중한 시간 방문해주셔서 감사합니다.

0개의 댓글