1. 모든 클래스 상속Object

Java의 모든 클래스는 암시적으로 Object클래스를 상속합니다.

Java Core 탐구에서 상속이 무엇이며 Java에서 어떻게 작동하는지 분석할 것입니다. 지금은 다음과 같은 간단한 사실 하나를 고려할 것입니다.

모든 클래스의 개체를 Object변수에 할당할 수 있습니다. 예:

암호 메모
Object o = new Scanner(System.in);
변수 는 개체 o에 대한 참조를 저장합니다.Scanner
Object o = new String();
변수 는 개체 o에 대한 참조를 저장합니다.String
Object o = new Integer(15);
변수 는 개체 o에 대한 참조를 저장합니다.Integer
Object o = "Hello";
변수 는 개체 o에 대한 참조를 저장합니다.String

이것은 좋은 소식이 끝나는 곳입니다. 컴파일러는 변수 에 저장된 개체의 원래 유형을 추적하지 않으므로 저장된 개체에서 클래스 의 메서드 이외의 메서드를 호출할 수 없습니다 .Object Object

개체의 원래 유형과 연결된 메서드를 호출해야 하는 경우 먼저 해당 개체에 대한 참조를 올바른 유형의 변수에 저장한 다음 해당 변수에 대한 메서드를 호출해야 합니다.

암호 메모
Object o = new Scanner(System.in);
int x = o.nextInt();
프로그램이 컴파일되지 않습니다. 클래스 에는 메서드가 Object없습니다 nextInt().
Object o = new Scanner(System.in);

Scanner console = (Scanner) o;

int x = console.nextInt();
작동합니다. 여기에서는 typecast 연산자를 사용하여 객체

에 대한 참조를 변수 Scanner에 저장합니다 . Scanner

변수가 개체 참조를 저장 Object하더라도 스캐너 변수에 변수를 할당할 수는 없습니다 . ObjectScanner그러나 이미 알고 있는 typecast 연산자를 사용하면 이 작업을 수행할 수 있습니다 . 일반적인 모습은 다음과 같습니다.

Type name1 = (Type) name2;

여기서 name1는 변수 이름 Type이고 는 개체 에 대한 참조를 저장하는 변수 name2이름입니다 .ObjectType

타입캐스팅

변수의 유형과 객체의 유형이 일치하지 않으면 a가 ClassCastException발생합니다. 예:

암호 메모
Object o = new Integer(5);
String s = (String) o;
런타임 시 오류가 발생합니다. 여기에서
a가 ClassCastException발생합니다.

Java에서 이 오류를 방지하는 방법이 있습니다. 변수에 저장된 개체의 유형을 확인하여 이를 수행합니다 .

name instanceof Type

연산자 는 변수가 객체인지 instanceof여부를 확인합니다 .nameType

예를 들어 다양한 개체의 배열에서 문자열을 찾아보겠습니다.

암호 메모
Object[] objects = {10, "Hello", 3.14};

for (int i = 0; i < objects.length; i++)
{
   if (objects[i] instanceof String)
   {
      String s = (String) objects[i];
      System.out.println(s);
   }
}
IntegerAutoboxing은 이 값을 각각 , String및 로 변환합니다 Double.

객체의 배열을 반복합니다.

객체가 a이면 변수 String

에 저장합니다 . 변수를 화면에 표시합니다. String


2. 제네릭이 등장한 이유 — 컬렉션

컬렉션으로 돌아가 봅시다.

Java 개발자는 클래스를 만들자마자 ArrayList모든 유형의 개체를 저장할 수 있도록 범용으로 만들고 싶었습니다. Object그래서 그들은 요소를 저장하기 위해 s 의 배열을 사용했습니다 .

이 접근 방식의 장점은 모든 유형의 개체를 컬렉션에 추가할 수 있다는 것입니다.

물론 몇 가지 약점이 있다.

단점 1.

컬렉션에서 요소를 검색할 때 항상 형식 변환 연산자를 작성해야 했습니다.

암호 메모
ArrayList numbers = new ArrayList();


for (int i = 0; i < 10; i++)
   numbers.add(i * 10);


int sum = 0;
for (int i = 0; i < 10; i++)
{
   sum = sum + (Integer) numbers.get(i);
}
Object개체 에 대한 참조를 저장하기 위한 컬렉션 만들기 컬렉션을

숫자 10, 20, ... 로 채웁니다 100.



컬렉션의 요소 합계


Typecasting이 필요합니다.

단점 2.

컬렉션에 특정 유형의 요소가 포함되어 있다는 보장이 없습니다.

암호 메모
ArrayList numbers = new ArrayList();


for (int i = 0; i < 10; i++)
   numbers.add(i * 2.5);


int sum = 0;
for (int i = 0; i < 10; i++)
{
   sum = sum + (Integer) numbers.get(i);
}
Object개체 에 대한 참조를 저장하는 컬렉션 만들기 개체

로 표시된 숫자로 컬렉션을 채웁니다 . , , , ... 컬렉션의 요소 합계 오류 가 발생합니다.Double
0.02.55.0





DoubleInteger

데이터는 어디에서나 컬렉션에 넣을 수 있습니다.

  • 다른 방법으로
  • 다른 프로그램에서
  • 파일에서
  • 네트워크를 통해

단점 3.

컬렉션의 데이터는 실수로 변경될 수 있습니다.

데이터로 채워진 컬렉션을 일부 메서드에 전달할 수 있습니다. 다른 프로그래머가 작성한 이 메서드는 해당 데이터를 컬렉션에 추가합니다.

컬렉션의 이름은 여기에 저장할 수 있는 데이터 유형을 명확하게 나타내지 않습니다. 그리고 변수에 명확한 이름을 지정하더라도 변수에 대한 참조가 12가지 메서드에 전달될 수 있으며 해당 메서드는 변수의 원래 이름에 대해 전혀 알지 못합니다.


3. 제네릭

자바의 제네릭

Java에서는 이러한 모든 문제가 제네릭이라는 멋진 기능으로 제거됩니다.

Java에서 제네릭은 유형에 유형 매개변수를 추가하는 기능을 의미합니다. 결과는 복합 복합 유형입니다. 이러한 복합 유형의 일반적인 보기는 다음과 같습니다.

ClassName<TypeParameter>

일반 클래스입니다. 그리고 평소에 클래스를 사용하는 모든 곳에서 사용할 수 있습니다.

암호 설명
ArrayList<Integer> list;
변수 만들기
list = new ArrayList<Integer> ();
개체 만들기
ArrayList<Integer>[] array;
어레이 생성

Integer이러한 컬렉션에는 변수 만 저장할 수 있습니다.

암호 설명
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(new Integer(1));
list.add(2);
list.add("Hello");
ArrayListInteger요소 가 포함된 컬렉션
이것은 허용되며
이것도 작동합니다.
오토박싱

그러나 이것은 허용되지 않습니다: 컴파일 오류

Java Collections 퀘스트에서 유형 매개변수를 사용하여 자신만의 클래스를 생성하는 방법을 배웁니다. 지금은 사용 방법과 작동 방식을 살펴보겠습니다.


4. 제네릭 작동 방식

실제로 제네릭은 매우 원시적입니다.

컴파일러는 단순히 제네릭 형식을 일반 형식으로 바꿉니다. 그러나 제네릭 형식의 메서드를 사용하는 경우 컴파일러는 매개 변수를 형식 매개 변수로 캐스팅하기 위해 형식 변환 연산자를 추가합니다.

암호 컴파일러가 하는 일
ArrayList<Integer> list = new ArrayList<Integer>();
ArrayList list = new ArrayList();
list.add(1);
list.add( (Integer) 1 );
int x = list.get(0);
int x = (Integer) list.get(0);
list.set(0, 10);
list.set(0, (Integer) 10);

정수 컬렉션의 숫자를 합산하는 메서드가 있다고 가정합니다.

암호 컴파일러가 하는 일
public int sum(ArrayList<Integer> numbers)
{
   int result = 0;

   for (int i = 0; i < numbers.size(); i++)
      result = result + numbers.get(i);

   return result;
}
public int sum(ArrayList numbers)
{
   int result = 0;

   for (int i = 0; i < numbers.size(); i++)
      result = result + (Integer) numbers.get(i);

   return result;
}

즉, 제네릭은 autoboxing과 비슷하지만 조금 더 많은 구문 설탕의 일종입니다. int오토박싱을 통해 컴파일러는 an을 an으로 Integer또는 그 반대로 변환하는 메서드를 추가 하고 제네릭의 경우 typecast 연산자를 추가합니다.

컴파일러가 유형 매개변수를 사용하여 일반 클래스를 컴파일한 후 일반 클래스 및 유형 변환 연산자로 간단히 변환됩니다. 제네릭 형식의 변수에 전달된 형식 인수에 대한 정보가 손실됩니다. 이 효과를 유형 삭제 라고도 합니다 .

때로 일반 클래스(유형 매개변수가 있는 클래스)를 작성하는 프로그래머는 실제로 인수로 전달된 유형에 대한 정보가 필요합니다. Java Collections 퀘스트에서 이를 처리하는 방법과 수반되는 사항을 배우게 됩니다.



5. 제네릭에 대한 몇 가지 사실

다음은 제네릭에 대한 몇 가지 흥미로운 사실입니다.

클래스는 여러 유형 매개변수를 가질 수 있습니다. 다음과 같이 보입니다.

ClassName<TypeParameter1, TypeParameter2, TypeParameter3>

사실 이것은 별로 놀라운 일이 아닙니다. 컴파일러가 하나의 유형으로 캐스트할 연산자를 추가할 수 있는 모든 곳에서 여러 유형 캐스트 ​​연산자를 추가할 수 있습니다.

예:

암호 메모
HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put(7, "Hello");
map.put(-15, "Hello");
메서드 put의 첫 번째 매개변수는 Integer이고 두 번째 매개변수는String

제네릭 유형도 매개변수로 사용할 수 있습니다 . 다음과 같이 보입니다.

ClassName<TypeParameter<TypeParameterParameter>>

문자열 목록을 저장할 목록을 만들고 싶다고 가정합니다. 이 경우 다음과 같은 결과를 얻게 됩니다.

// List of greetings
ArrayList<String> listHello = new ArrayList<String>();
listHello.add ("Hello");
listHello.add ("Hi");

// List of goodbyes
ArrayList<String> listBye = new ArrayList<String>();
listBye.add("Bye");
listBye.add ("Goodbye");

// List of lists
ArrayList<ArrayList<String>> lists = new ArrayList<ArrayList<String>>();
lists.add(listHello);
lists.add(listBye);

일반 유형(유형 매개변수가 있는 유형)도 배열 유형으로 사용할 수 있습니다. 다음과 같이 보입니다.

ClassName<TypeParameter>[] array = new ClassName<TypeParameter>[size];

여기에는 마법 같은 일이 일어나지 않습니다. 꺾쇠 괄호는 유형 이름을 나타냅니다.

암호 제네릭이 아닌 대응물
ArrayList<String>[] list = new ArrayList<String>[10];
StringArrayList[] list = new StringArrayList[10];
ArrayList<Integer>[] list = new ArrayList<Integer>[10];
IntegerArrayList[] list = new IntegerArrayList[10];
ArrayList<Scanner>[] list = new ArrayList<Scanner>[10];
ScannerArrayList[] list = new ScannerArrayList[10];