DevOps

Java 101 : 필수 Java 언어 기능 둘러보기, 1 부-2장 assertions and generics

IT오이시이 2017. 6. 14. 13:54
728x90
JAVA 101: THE NEXT GENERATION

Java 101: The essential Java language features tour, Part 1-2

Java programming with assertions and generics


http://www.javaworld.com/article/2071216/learn-java/article.html?page=2


Java 언어는 다음과 같은 종류의 실제 유형 인수를 지원합니다.

  • 구체적 유형 : 클래스 또는 기타 참조 유형 이름이 유형 매개 변수에 전달됩니다. 예를 들어,에 List<Animal>Animal에 전달됩니다 E.
  • 구체적 매개 변수화 된 형식 : 매개 변수화 된 형식 이름이 형식 매개 변수에 전달됩니다. 예를 들어,에 Set<List<Shape>>List<Shape>에 전달됩니다 E.
  • 배열 유형 : 배열이 type 매개 변수에 전달됩니다. 예를 들어,에 Map<String, String[]>String에 전달 K하고 String[]에 전달됩니다 V.
  • 유형 매개 변수 : 유형 매개 변수가 유형 매개 변수에 전달됩니다. 예를 들어,에 class Container<E> { Set<E> elements; }E에 전달됩니다 E.
  • 와일드 카드 : 물음표 ( ?)가 type 매개 변수에 전달됩니다. 예를 들어,에 Class<?>?에 전달됩니다 T.

각 generic 형식은 원시 형식 의 존재를 의미 하며 형식 형식 매개 변수 목록이없는 제네릭 형식입니다. 예를 들어, Class원시 유형이 Class<T>있습니다. 제네릭 형식과 달리 원시 형식은 모든 종류의 개체와 함께 사용할 수 있습니다.

제네릭 형식 선언 및 사용

제네릭 형식을 선언하는 데는 형식 형식 매개 변수 목록을 지정하고 해당 형식 매개 변수를 해당 구현을 통해 사용하는 것이 포함됩니다. 제네릭 형식을 사용하면 제네릭 형식을 인스턴스화 할 때 실제 형식 인수를 해당 형식 매개 변수에 전달해야합니다. Listing 5를 참조한다.

Listing 5. GenDemo.java (버전 1)

class Container<E>
{
   private E[] elements;
   private int index;

   Container(int size)
   {
      elements = (E[]) new Object[size];
      index = 0;
   }

   void add(E element)
   {
      elements[index++] = element;
   }

   E get(int index)
   {
      return elements[index];
   }

   int size()
   {
      return index;
   }
}

public class GenDemo
{
   public static void main(String[] args)
   {
      Container<String> con = new Container<String>(5);
      con.add(“North”);
      con.add(“South”);
      con.add(“East”);
      con.add(“West”);
      for (int i = 0; i < con.size(); i++)
         System.out.println(con.get(i));
   }
}

Listing 5는 적절한 인수 유형의 객체를 저장하는 간단한 컨테이너 유형의 컨텍스트에서 일반 유형 선언 및 사용법을 보여줍니다. 코드를 간단하게 유지하기 위해 오류 검사는 생략했습니다.

Container클래스는 지정하여 제네릭 형식을로 자신을 선언 <E>형식 형식 매개 변수 목록을. Type 매개 변수 E는 저장된 요소의 유형, 내부 배열에 추가 할 요소 및 요소를 검색 할 때의 반환 유형을 식별하는 데 사용됩니다.

Container(int size)생성자를 통해 상기 배열을 생성 elements = (E[]) new Object[size];. 왜 내가 지정하지 않았는지 궁금하다면 그 이유 elements = new E[size];는 불가능하다는 것입니다. 그렇게하면 a ClassCastException.

Listing 5 (컴파일 javac GenDemo.java). (E[])캐스트 출력 캐스트가 선택하지 않은 것에 대해 경고를 컴파일러가 발생합니다. 그것은 플래그에서 downcasting 가능성 Object[]에 대한 것이 E[]있기 때문에 유형의 안전을 위반할 수 Object[]개체의 모든 종류를 저장할 수 있습니다.

그러나이 예제에서는 형식 안전을 위반할 수있는 방법이 없습니다. E내부 배열에 비 객체 를 저장하는 것은 불가능 합니다. 다음 기사에서이 경고 메시지를 표시하지 않는 방법을 알려 드리겠습니다.

실행 java GenDemo이 응용 프로그램을 실행합니다. 다음 출력을 관찰해야합니다.

North
South
East
West

경계 유형 매개 변수

E에서이 Set<E>의 예입니다 바운드 형식의 형식 매개 변수 당신이 어떤 실제의 형태 인수를 전달할 수 있기 때문에 E. 예를 들어, 당신은 지정할 수 있습니다 Set<Marble>Set<Employee>또는 Set<String>.

경우에 따라 형식 매개 변수로 전달할 수있는 실제 형식 인수의 형식을 제한하려고합니다. 예를 들어, type 매개 변수를 accept Employee및 해당 하위 클래스 로 제한 하려고합니다.

사용자가 지정하여 입력 파라미터를 제한 할 수있는 상한을 실제의 입력 인자로 전달 될 수있는 유형의 상한로서의 형태이다. 예약어를 통해 상한을 지정하고 extends그 뒤에 상한의 유형 이름을 지정하십시오.

예를 들어, class Employees<E extends Employee>전달 될 수있는 유형의 제한 Employees에 Employee또는 서브 클래스 (예를 Accountant). 지정 new Employees<Accountant>하는 것은 합법적 인 반면 new Employees<String>불법입니다.

유형 매개 변수에 둘 이상의 상한을 지정할 수 있습니다. 그러나 첫 번째 경계는 항상 클래스 여야하며 추가 경계는 항상 인터페이스 여야합니다. 각 바운드는 앰퍼샌드 ( &) 에 의해 전임자와 구분됩니다 . Listing 6을 확인하자.

Listing 6. GenDemo.java (버전 2)

import java.math.BigDecimal;

import java.util.Arrays;

abstract class Employee
{
   private BigDecimal hourlySalary;
   private String name;

   Employee(String name, BigDecimal hourlySalary)
   {
      this.name = name;
      this.hourlySalary = hourlySalary;
   }

   public BigDecimal getHourlySalary()
   {
      return hourlySalary;
   }

   public String getName()
   {
      return name;
   }

   public String toString()
   {
      return name+”: “+hourlySalary.toString();
   }
}

class Accountant extends Employee implements Comparable<Accountant>
{
   Accountant(String name, BigDecimal hourlySalary)
   {
      super(name, hourlySalary);
   }

   public int compareTo(Accountant acct)
   {
      return getHourlySalary().compareTo(acct.getHourlySalary());
   }
}

class SortedEmployees<E extends Employee & Comparable<E>>
{
   private E[] employees;
   private int index;

   SortedEmployees(int size)
   {
      employees = (E[]) new Employee[size];
      int index = 0;
   }

   void add(E emp)
   {
      employees[index++] = emp;
      Arrays.sort(employees, 0, index);
   }

   E get(int index)
   {
      return employees[index];
   }

   int size()
   {
      return index;
   }
}

public class GenDemo
{
   public static void main(String[] args)
   {
      SortedEmployees<Accountant> se = new
SortedEmployees<Accountant>(10);
      se.add(new Accountant(“John Doe”, new BigDecimal(“35.40”)));
      se.add(new Accountant(“George Smith”, new BigDecimal(“15.20”)));
      se.add(new Accountant(“Jane Jones”, new BigDecimal(“25.60”)));

      for (int i = 0; i < se.size(); i++)
         System.out.println(se.get(i));
   }
}

Listing 6의 Employee클래스는 시간당 급여를받는 직원의 개념을 추상화한다. 이 클래스는 하위 클래스로 Accountant, s가 자연 순서에 따라 비교 될 수 있음 Comparable<Accountant>을 나타 내기 Accountant위해 구현됩니다.이 순서는이 예에서 시간별 임금입니다.

java.lang.Comparable인터페이스의 이름은 단일 유형의 매개 변수를 사용하여 제네릭 형식으로 선언됩니다 T. 이 클래스 int compareTo(T o)는, 현재의 객체와 인수의 형태 T를 비교해,이 객체가 지정된 객체보다 작은 경우는 부의 정수, 동일한 경우는 0, 큰 경우는 정의 정수를 돌려주는 메소드를 제공합니다.

이 SortedEmployees클래스를 사용하면 내부 배열 Employee에 구현 Comparable된 하위 클래스 인스턴스를 저장할 수 있습니다 . 이 배열은 서브 클래스 인스턴스가 추가 된 후 시간당 임금의 오름차순 으로 ( java.util.Arrays클래스의 void sort(Object[] a, int fromIndex, int toIndex)클래스 메소드 를 통해) 정렬 Employee됩니다.

Listing 6 ( javac GenDemo.java)을 컴파일 하고 애플리케이션 ( java GenDemo) 을 실행한다 . 다음 출력을 관찰해야합니다.

George Smith: 15.20
Jane Jones: 25.60
John Doe: 35.40

하한은 어떨까요?

제네릭 형식 매개 변수의 하한을 지정할 수 없습니다. 왜 내가 Angelika Langer의 Java Generics FAQ 를 "하찮은 것이고 특별히 도움이되지 않을 것"이라고 말하는 낮은 범위의 주제에 대해 읽는 것이 좋은지 이해하는 것.

와일드 카드 고려하기

이러한 개체가 문자열, 직원, 도형 또는 기타 형식인지 여부에 관계없이 개체 목록을 인쇄하려고한다고 가정 해 봅시다. 첫 번째 시도는 Listing 7과 같다.

Listing 7. GenDemo.java (버전 3)

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class GenDemo
{
   public static void main(String[] args)
   {
      List<String> directions = new ArrayList<String>();
      directions.add(“north”);
      directions.add(“south”);
      directions.add(“east”);
      directions.add(“west”);
      printList(directions);

      List<Integer> grades = new ArrayList<Integer>();
      grades.add(new Integer(98));
      grades.add(new Integer(63));
      grades.add(new Integer(87));
      printList(grades);
   }

   static void printList(List<Object> list)
   {
      Iterator<Object> iter = list.iterator();
      while (iter.hasNext())
         System.out.println(iter.next());
   }
}

문자열 목록이나 정수 목록은 객체 목록의 하위 유형이지만 컴파일러는이 목록을 컴파일하려고하면 불만을 나타냅니다. 특히 그것은 list-of-string이 list-of-object로 변환 될 수없고 integer-of-list와 마찬가지로 변환 될 수 있음을 알려줍니다.

받은 오류 메시지는 제네릭의 기본 규칙과 관련되어 있습니다.

y 유형의 지정된 부속 유형 x 에 대해 G 를 원시 유형 선언으로 지정하면 G <x> 는 G <y> 의 부속 유형이 아닙니다 .

하지만,이 규칙에 따르면 String과 java.lang.Integer의 하위 유형이있다 java.lang.Object, 그것은 사실이 아니에요 List<String>과 List<Integer>의 하위 유형이 있습니다 List<Object>.

왜 우리는이 규칙을 가지고 있습니까? 제네릭은 컴파일 타임에 유형 안전 위반 사항을 파악하도록 설계되었으므로 도움이됩니다. 제네릭 없이는 Java 프로그램이 중단 ClassCastException되어 충돌 이 발생하여 오전 2시에 작업하라는 요청이 훨씬 많이 발생할 수 있습니다 !

데모로, 이제 그 가정 해 봅시다 List<String> 있었다 의 하위 유형 List<Object>. 이것이 사실이라면 다음 코드로 끝날 수 있습니다.

List<String> directions = new ArrayList<String>();
List<Object> objects = directions;
objects.add(new Integer());
String s = objects.get(0);

이 코드 조각은 배열 목록을 기반으로 문자열 목록을 만듭니다. 그런 다음이 목록을 객체 목록에 업 캐스팅합니다 (이는 합법이 아니지만 지금은 그냥 그대로있는 것입니다). 그런 다음 객체 목록에 정수를 추가하여 유형 안전을 위반합니다. ClassCastException저장된 정수를 문자열로 캐스트 할 수 없기 때문에 문제가 발생 합니다.

generics가 없다면 유형 안전성 위반을 막을 수있는 유일한 방법 은 유형 7 List<Object>의 printList()메소드에 유형의 오브젝트를 전달하는 것이고 이는 그리 유용하지 않을 것이다. 그러나 generics 를 사용하면 Listing 8과 같이 와일드 카드를 사용하여 문제를 해결할 수있다.

Listing 8. GenDemo.java (버전 4)

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class GenDemo
{
   public static void main(String[] args)
   {
      List<String> directions = new ArrayList<String>();
      directions.add(“north”);
      directions.add(“south”);
      directions.add(“east”);
      directions.add(“west”);
      printList(directions);

      List<Integer> grades = new ArrayList<Integer>();
      grades.add(new Integer(98));
      grades.add(new Integer(63));
      grades.add(new Integer(87));
      printList(grades);
   }

   static void printList(List<?> list)
   {
      Iterator<?> iter = list.iterator();
      while (iter.hasNext())
         System.out.println(iter.next());
   }
}

목록 8에서는 내가 사용하는 와일드 카드 합니다 ( ?대신에 기호) Object에서 printList()의 매개 변수 목록과 몸. 이 기호는 모든 유형을 의미하기 때문에, 전달하는 합법적 List<String>및 List<Integer>이 방법에.

Listing 8 ( javac GenDemo.java)을 컴파일 하고 애플리케이션 ( java GenDemo) 을 실행한다 . 다음 출력을 관찰해야합니다.

north
south
east
west
98
63
87

일반적인 방법 발견하기

이제 필터 목록에 따라 개체 목록을 다른 목록에 복사한다고 가정 해 보겠습니다. void copy(List<Object> src, List<Object> dst, Filter filter)메소드를 선언하는 것을 고려할 수도 있지만,이 메소드는 Lists의 Objects 만을 복사 할 수 있습니다 .

임의 유형의 소스 및 대상 목록을 전달하려면 유형 자리 표시 자에 대해 와일드 카드를 사용해야합니다. 예를 들어, 다음 copy()메소드를 고려하십시오 .

void copy(List<?> src, List<?> dest, Filter filter)
{
   for (int i = 0; i < src.size(); i++)
      if (filter.accept(src.get(i)))
         dest.add(src.get(i));
}

이 메소드의 매개 변수 목록은 정확하지만 문제가 있습니다. 컴파일러에 따르면 dest.add(src.get(i));유형 안전을 위반합니다. 이는 ?모든 종류의 객체가 목록의 요소 유형이 될 수 있으며 소스와 대상 요소 유형이 호환되지 않을 수 있음을 의미합니다.

예를 들어, 소스 목록이었다 List의 Shape와 대상 목록이 있었다 List의 String, 그리고 copy()진행시키고 ClassCastException대상 목록의 요소를 검색 할 때 발생된다.

다음과 같이 와일드 카드의 상한 및 하한을 제공하여이 문제를 부분적으로 해결할 수 있습니다.

void copy(List<? extends String> src, List<? super String> dest,
Filter filter)
{
   for (int i = 0; i < src.size(); i++)
      if (filter.accept(src.get(i)))
         dest.add(src.get(i));
}




728x90
반응형