JavaRush /Java Blog /Random-KO /Java의 제네릭이란 무엇입니까?

Java의 제네릭이란 무엇입니까?

Random-KO 그룹에 게시되었습니다
안녕하세요! 오늘은 제네릭에 대해 이야기하겠습니다. 나는 당신이 많은 새로운 것을 배울 것이라고 말해야합니다! 이것뿐만 아니라 다음 강의에서도 제네릭에 대해 집중적으로 다루겠습니다. Java의 제네릭이란 무엇입니까 - 1 그러므로 이 주제가 당신에게 흥미가 있다면 당신은 운이 좋은 것입니다. 오늘 당신은 제네릭의 기능에 대해 많은 것을 배울 것입니다. 그렇지 않다면 진정하고 휴식을 취하세요! :) 이것은 매우 중요한 주제이므로 알아야 합니다. "무엇"과 "왜"라는 간단한 것부터 시작해 보겠습니다. 제네릭이란 무엇입니까? 제네릭은 매개변수가 있는 유형입니다. 원본을 생성할 때 해당 유형뿐만 아니라 작업할 데이터 유형도 지정합니다. 가장 분명한 예가 이미 여러분 마음 속에 떠올랐다고 생각합니다. 이것이 바로 ArrayList입니다! 프로그램에서 일반적으로 생성하는 방법은 다음과 같습니다.
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<String> myList1 = new ArrayList<>();
       myList1.add("Test String 1");
       myList1.add("Test String 2");
   }
}
짐작할 수 있듯이 목록의 특징은 모든 것을 목록에 "채울" 수 없다는 것입니다. 목록은 개체에만 작동합니다 String. 이제 Java의 역사를 잠시 살펴보고 "왜?"라는 질문에 답해 보겠습니다. 이를 위해 ArrayList 클래스의 단순화된 버전을 직접 작성하겠습니다. 우리 목록은 내부 배열에만 데이터를 추가하고 다음 데이터를 받을 수 있습니다.
public class MyListClass {

   private Object[] data;
   private int count;

   public MyListClass() {
       this.data = new Object[10];
       this.count = 0;
   }

   public void add(Object o) {
       this.data[count] = o;
       count++;
   }

   public Object[] getData() {
       return data;
   }
}
목록에 숫자만 저장하기를 원한다고 가정해 보겠습니다 Integer. 우리는 제네릭이 없습니다. Integer에서 check의 o 인스턴스를 명시적으로 지정할 수 없습니다 add(). 그러면 우리의 전체 클래스는 에만 적합할 것이며 Integer세상에 존재하는 모든 데이터 유형에 대해 동일한 클래스를 작성해야 할 것입니다! 우리는 프로그래머에게 의존하기로 결정하고 그들이 불필요한 것을 추가하지 않도록 코드에 주석을 남기기로 결정했습니다.
//use it ONLY with Integer data type
public void add(Object o) {
   this.data[count] = o;
   count++;
}
프로그래머 중 한 명이 이 설명을 놓치고 실수로 문자열과 혼합된 숫자를 목록에 넣은 다음 그 합계를 계산하려고 했습니다.
public class Main {

   public static void main(String[] args) {

       MyListClass list = new MyListClass();
       list.add(100);
       list.add(200);
       list.add("Lolkek");
       list.add("Shalala");

       Integer sum1 = (Integer) list.getData()[0] + (Integer) list.getData()[1];
       System.out.println(sum1);

       Integer sum2 = (Integer) list.getData()[2] + (Integer) list.getData()[3];
       System.out.println(sum2);
   }
}
콘솔 출력: 300 스레드 "main"의 예외 java.lang.ClassCastException: java.lang.String은 Main.main(Main.java:14)에서 java.lang.Integer로 캐스팅할 수 없습니다. 이 상황에서 최악의 상황은 무엇입니까? 프로그래머의 부주의함과는 거리가 멀다. 최악의 상황은 잘못된 코드가 우리 프로그램의 중요한 위치에 들어가 성공적으로 컴파일되었다는 것 입니다 . 이제 우리는 코딩 단계가 아닌 테스트 단계에서만 오류를 보게 될 것입니다(이것이 가장 좋은 경우입니다!). 나중에 개발 단계에서 버그를 수정하려면 돈과 시간 모두 훨씬 더 많은 비용이 듭니다. 이것이 바로 제네릭의 장점입니다. 제네릭 클래스를 사용하면 불운한 프로그래머가 오류를 즉시 감지할 수 있습니다. 코드가 컴파일되지 않습니다!
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Integer> myList1 = new ArrayList<>();

       myList1.add(100);
       myList1.add(100);
       myList1.add("Lolkek");//error!
       myList1.add("Shalala");//error!
   }
}
프로그래머는 즉시 "정신을 차리고" 즉각적으로 자신을 바로잡을 것입니다. 그런데 List이런 종류의 오류를 보기 위해 자체 클래스를 만들 필요는 없었습니다. <Integer>일반 ArrayList에서 유형 괄호( )를 제거하기만 하면 됩니다 !
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

      List list = new ArrayList();

      list.add(100);
      list.add(200);
      list.add("Lolkek");
      list.add("Shalala");

       System.out.println((Integer) list.get(0) + (Integer) list.get(1));
       System.out.println((Integer) list.get(2) + (Integer) list.get(3));
   }
}
콘솔 출력: 300 "main" 스레드의 예외 java.lang.ClassCastException: java.lang.String은 Main.main(Main.java:16)에서 java.lang.Integer로 캐스팅할 수 없습니다. 즉, "네이티브" 도구를 사용하는 경우에도 마찬가지입니다. Java에서는 이런 실수를 해서 안전하지 않은 컬렉션을 만들 수 있습니다. 그러나 이 코드를 IDEa에 붙여 넣으면 다음과 같은 경고가 표시됩니다. " java.util.List의 원시 유형 멤버로서 add(E)에 대한 확인되지 않은 호출 " 이는 요소를 IDEa에 추가할 때 문제가 발생할 수 있음을 알려줍니다. 제네릭 없는 컬렉션은 이렇지 않습니다. 그런데 "원시 유형"이라는 문구는 무엇을 의미합니까? 문자 그대로의 번역은 " raw type " 또는 " dirty type " 과 같이 매우 정확합니다 . Raw type해당 유형이 제거된 일반 클래스입니다. 즉, List myList1이것은 Raw type. 그 반대는 유형 사양을 사용하여 올바르게 생성된 일반 클래스( class 라고도 함 ) raw type입니다 . 예를 들어, . 질문이 있을 수 있습니다. 왜 사용이 허용됩니까 ? 이유는 간단합니다. Java 제작자는 호환성 문제를 일으키지 않기 위해 언어 지원을 떠났습니다 . Java 5.0이 출시되었을 때(이 버전에서 처음으로 제네릭이 등장함) 많은 코드가 이미 . 그러므로 이러한 가능성은 오늘날에도 여전히 존재합니다. 우리는 이미 강의에서 Joshua Bloch의 고전 책인 "Effective Java"를 여러 번 언급했습니다. 언어 창시자 중 한 사람으로서 그는 책에서 와 사용이라는 주제를 무시하지 않았습니다 . 이 책의 23장은 “새 코드에서 원시 유형을 사용하지 마십시오”라는 매우 설득력 있는 제목을 가지고 있습니다. 이것은 여러분이 기억해야 할 사항입니다. 일반 클래스를 사용할 때는 절대로 . generic typeparameterized typeList<String> myList1raw typesraw typesraw typesraw typesgeneric typesJava의 제네릭이란 무엇입니까 - 2generic typeraw type

유형화된 메소드

Java를 사용하면 개별 메서드를 입력하여 소위 일반 메서드를 만들 수 있습니다. 그러한 방법이 왜 편리한가요? 우선, 다양한 유형의 매개변수로 작업할 수 있기 때문입니다. 동일한 논리를 다양한 유형에 안전하게 적용할 수 있다면 제네릭 메서드가 훌륭한 솔루션입니다. 예를 살펴보겠습니다. 어떤 종류의 목록이 있다고 가정해 보겠습니다 myList1. 우리는 모든 값을 제거하고 모든 여유 공간을 새로운 값으로 채우고 싶습니다. 일반적인 메소드를 사용하는 클래스는 다음과 같습니다.
public class TestClass {

   public static <T> void fill(List<T> list, T val) {
       for (int i = 0; i < list.size(); i++)
           list.set(i, val);
   }

   public static void main(String[] args) {

       List<String> strings = new ArrayList<>();
       strings.add("Старая строка 1");
       strings.add("Старая строка 2");
       strings.add("Старая строка 3");

       fill(strings, "Новая строка");

       System.out.println(strings);

       List<Integer> numbers = new ArrayList<>();
       numbers.add(1);
       numbers.add(2);
       numbers.add(3);

       fill(numbers, 888);
       System.out.println(numbers);
   }
}
구문에 주의하세요. 약간 이상해 보입니다.
public static <T> void fill(List<T> list, T val)
반환 유형 앞에는 일반 메서드를 나타내는 <T>가 옵니다. 이 경우 메소드는 2개의 매개변수를 입력으로 사용합니다. 즉, 객체 목록 T와 또 다른 별도의 객체 T입니다. <T>를 사용하면 메소드 입력이 이루어집니다. 즉, 문자열 목록과 숫자를 전달할 수 없습니다. 문자열과 문자열의 목록, 숫자와 숫자의 목록, 개체 목록 Cat과 다른 개체 목록 Cat- 그게 유일한 방법입니다. 이 방법은 이 방법이 다양한 유형의 데이터에 쉽게 작동한다는 main()것을 명확하게 보여줍니다 . fill()먼저 문자열 목록과 문자열을 입력으로 받은 다음 숫자 목록과 숫자를 입력으로 받습니다. 콘솔 출력: [Newline, Newline, Newline] [888, 888, 888]fill() 30개의 서로 다른 클래스에 대한 메서드 로직이 필요하고 일반 메서드가 없다고 상상해 보세요 . 서로 다른 데이터 유형에 대해서만 동일한 메소드를 30번 작성해야 합니다! 하지만 일반적인 방법 덕분에 코드를 재사용할 수 있습니다! :)

유형화된 클래스

Java에서 제공되는 일반 클래스를 사용할 수 있을 뿐만 아니라 자신만의 클래스를 만들 수도 있습니다! 간단한 예는 다음과 같습니다.
public class Box<T> {

   private T t;

   public void set(T t) {
       this.t = t;
   }

   public T get() {
       return t;
   }

   public static void main(String[] args) {

       Box<String> stringBox = new Box<>();

       stringBox.set("Старая строка");
       System.out.println(stringBox.get());
       stringBox.set("Новая строка");

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

       stringBox.set(12345);//ошибка компиляции!
   }
}
우리 클래스 Box<T>("상자")가 입력되었습니다. 생성 중에 데이터 유형( )을 할당하면 <T>더 이상 다른 유형의 개체를 여기에 배치할 수 없습니다. 이는 예에서 볼 수 있습니다. 생성할 때 객체가 문자열과 함께 작동하도록 지정했습니다.
Box<String> stringBox = new Box<>();
그리고 코드의 마지막 줄에서 상자 안에 숫자 12345를 넣으려고 하면 컴파일 오류가 발생합니다! 그렇게 해서 우리는 우리만의 일반 클래스를 만들었습니다! :) 이것으로 오늘 강의를 마치겠습니다. 그러나 우리는 제네릭에 작별 인사를 하는 것이 아닙니다! 다음 강의에서는 더 많은 고급 기능에 대해 이야기할 테니 작별 인사는 하지 마세요! ) 공부에 행운이 있기를 바랍니다! :)
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION