DevOps

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

IT오이시이 2017. 6. 14. 12:35
728x90


JAVA 101: THE NEXT GENERATION

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

Java programming with assertions and generics


원문 : http://www.javaworld.com/article/2071216/learn-java/article.html


JDK 1.4의 주장에서 Java 8의 향후 람다에 이르기까지 Java 언어는 처음부터 상당히 진화 해 왔습니다. 다음 몇 가지 Java 101 기사는 필수 Java 언어 기능의 도구 상자를 제시하며 이번 주부터 주장 및 제네릭으로 시작합니다.

Java 언어는 세계에서 가장 널리 사용되는 프로그래밍 언어 중 하나입니다. Java 101 의 다음 기사 : 차세대에서는 Java 1.4에서 Java 8까지 Java 언어에 추가 된 기능에 중점을  것입니다 (Java 9에서도 터치 할 수 있습니다). 필자의 목표는 Java 프로그램에서 왜 그리고 어떻게 사용되는지를 보여주는 예제와 함께 필수적인 Java 언어 기능의 도구 상자를 소개하는 것입니다.

이 첫 번째 기사는 Java 1.4에 추가 된 어설 션 및 Java 5에서 소개 된 소수의 중요한 새 기능 중 첫 번째 인 generics에 관한 것입니다.

Java 1.4의 어설 션

어설 자바 1.4의 도입은, Java 언어에 가장 유용하고 중요한 추가 중 하나 남아있다. 어설 션은 프로그램을 올바르게 만들거나 없게 만드는 요구 사항을 성문화하는 데 사용됩니다. 어설 테스트 조건 ( 일명 이러한 조건이 거짓 일 때 개발자를 알리는 진정한 값에 대한 부울 식을). 어설 션을 사용하면 코드의 정확성에 대한 자신감을 크게 높일 수 있습니다.

Java Q & A 블로그

Java 프로그래밍에 대해 궁금한 점이 있습니까? 경험 많은 Java 교사로부터 신뢰할 수있는 답변을 얻으십시오. Jeff Friesen의 Java Q & A 블로그 는 매주 발행되어 Java 초보자 및 숙련 된 개발자의 일반적인 프로그래밍 문제를 해결합니다.

어설 션은 프로그램 테스트를 위해 설정 한 것으로 가정 할 때 런타임에 실행되는 컴파일 가능한 엔터티입니다. 어설 션을 프로그래밍하여 버그가 발생한 지점에서 버그를 알리면 실패한 프로그램을 디버깅하는 데 소요되는 시간을 크게 줄일 수 있습니다.

Java 1.4 이전에는 개발자가 주로 코드 정확성에 대한 가정을 문서화하기 위해 주석을 사용했습니다. 코드를 문서화하는 데 유용하지만 주석은 테스트 및 디버깅 메커니즘처럼 어설 션보다 열등합니다. 컴파일러는 주석을 무시하기 때문에 버그 알림에 사용할 수 없습니다. 코드가 변경 되더라도 주석은 변경되지 않는 것이 일반적입니다.

어설션 구현하기

주장은 통해 구현되는 assert문 및 java.lang.AssertionError클래스입니다. 이 명령.은 키워드로 시작 assert하고 부울 표현식으로 계속됩니다. assert다음과 같이 문은 구문 적으로 표현된다 :

assert BooleanExpr;

경우 BooleanExpr이 true로 평가되면 아무 일도 일어나지 않고 실행이 계속됩니다. 그러나 표현식이 false로 평가되면 Listing 1과 같이 AssertionError가 인스턴스화되고 Throw된다.

Listing 1. AssertDemo.java (버전 1)

public class AssertDemo
{
   public static void main(String[] args)
   {
      int x = -1;
      assert x >= 0;
   }
}

Listing 1의 주장은 변수 x가 0보다 크거나 같은 값을 가지고 있다는 개발자의 믿음을 나타낸다. 그러나 assert 문의 실행으로 인해 AssertionError가 발생할때이것은 분명하지 않습니다.

Listing 1 (javac AssertDemo.java)을 컴파일하고 assertions가 활성화 된 상태에서 실행한다 (java -ea AssertDemo). 다음 출력을 관찰해야합니다.

Exception in thread main java.lang.AssertionError
    at AssertDemo.main(AssertDemo.java:6)

이 메시지는 AssertionError의 원인을 식별하지 못하기 때문에 다소 모호 합니다. 좀 더 유익한 메시지를 assert원한다면 아래 에 표시된 문장을 사용하십시오 :

assert BooleanExpr : expr; 

여기에서 expr은 값을 반환 할 수있는 모든 식 (메서드 호출 포함)입니다. void 반환 형식의 메서드를 호출 할 수 없습니다. 유용한 표현식은 Listing 2와 같이 실패 이유를 설명하는 문자열 리터럴입니다.

Listing 2. AssertDemo.java (버전 2)

public class AssertDemo
{
   public static void main(String[] args)
   {
      int x = -1;
      assert x >= 0: x < 0”;
   }
}

Listing 2 ( javac AssertDemo.java)를 컴파일 하고 assertions enabled ( java -ea AssertDemo)로 실행한다 . 이번에는 다음과 같이 약간 확장 된 출력을 관찰해야합니다. 출력에는  AssertionError throw 된 이유가 포함됩니다.

Exception in thread main java.lang.AssertionError: x < 0
    at AssertDemo.main(AssertDemo.java:6)

두 예제 모두 (enable assertions) 옵션 AssertDemo없이 실행 -ea하면 결과가 출력되지 않습니다. assertion가 유효하지 않은 경우, assertion는 여전히 클래스 파일에 존재하지만 실행되지 않습니다.

어설션을 통한 전제 조건 및 사후 조건 테스트

어설 션은 종종 프로그램의 전제 조건 및 사후 조건을 테스트하는 데 사용됩니다.

  • 전제 조건이 몇 가지 코드 시퀀스의 실행 전에 true로 평가해야하는 조건이다. 전제 조건은 발신자가 수신자와 계약을 유지할 수 있도록 합니다.
  • 사후 조건은 몇 가지 코드 시퀀스의 실행 후 true로 평가해야하는 조건이다. 사후 조건은 피 호출자가 발신자와 계속 계약을 유지하도록 합니다.

전제 조건 (Preconditions)

확실한 체크를 실시해, 필요에 따라서 예외를 슬로우하는 것에 의해, public constructor 및 메소드에 전제 조건을 적용 할 수 있습니다. 개인 도우미 메서드의 경우 어설 션을 지정하여 전제 조건을 적용 할 수 있습니다. Listing 3을 살펴 보자.

Listing 3. AssertDemo.java (버전 3)

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;

class PNG
{
   /**
    *  Create a PNG instance, read specified PNG file, and decode
    *  it into suitable structures.
    *
    *  @param filespec path and name of PNG file to read
    *
    *  @throws NullPointerException when <code>filespec</code>
is
    *          <code>null</code>
    */
   PNG(String filespec) throws IOException
   {
      // Enforce preconditions in non-private constructors and
      // methods.

      if (filespec == null)
         throw new NullPointerException(“filespec is null”);
      try (FileInputStream fis = new FileInputStream(filespec))
      {
         readHeader(fis);
      }
   }

   private void readHeader(InputStream is) throws IOException
   {
      // Confirm that precondition is satisfied in private
      // helper methods.

      assert is != null : null passed to is”;
   }
}

public class AssertDemo
{
   public static void main(String[] args) throws IOException
   {
      PNG png = new PNG((args.length == 0) ? null : args[0]);
   }
}

Listing 3 의 PNG클래스는 PNG (portable network graphics) 이미지 파일을 읽고 디코딩하는 라이브러리의 최소 시작 부분이다. 생성자는  명시 적으로 filespec와 null을 비교 하여, 이 매개 변수가  null을 포함될 때 NullPointerException을 throw합니다 . 요점은 filespec이 null을 포함하지 않는 전제 조건을 강요하는 것입니다 .

assert filespec != null;assertion가 무효화되었을 때 생성자의 Javadoc에 언급 된 전제 조건이 (기술적으로) 존중되지 않기 때문에 지정 하는 것이 적절하지 않습니다. (실제로 FileInputStream()던져 버리기 때문에  NullPointerException  유용하지만, 문서화되지 않은 행동에 의존해서는 안됩니다.)

그러나 assert 자체는  메서드 readHeader()의 컨텍스트 에서는 적절하고, PNG 파일의 8 바이트 헤더를 읽고 디코딩하도록 결국 완료됩니다. 전제 조건 is는 항상 null이 아닌 값이 전달되어 유지됩니다.

사후 조건 (Postconditions)

사후 조건은 일반적으로 메서드 (또는 생성자)가 public인지 여부에 관계없이 어설션을 통해 지정됩니다. 이것을 Listing 4를 통해 살펴 봅니다.

Listing 4. AssertDemo.java (버전 4)

public class AssertDemo
{
   public static void main(String[] args)
   {
      int[] array = { 20, 91, -6, 16, 0, 7, 51, 42, 3, 1 };
      sort(array);
      for (int element: array)
         System.out.printf(“%d “, element);
      System.out.println();
   }

   private static boolean isSorted(int[] x)
   {
      for (int i = 0; i < x.length-1; i++)
         if (x[i] > x[i+1])
            return false;
      return true;
   }

   private static void sort(int[] x)
   {
      int j, a;
      // For all integer values except the leftmost value ...
      for (int i = 1; i < x.length; i++)
      {
         // Get integer value a.
         a = x[i];
         // Get index of a. This is the initial insert position, which is
         // used if a is larger than all values in the sorted section.
         j = i;
         // While values exist to the left of a’s insert position and the
         // value immediately to the left of that insert position is
         // numerically greater than a’s value ...
         while (j > 0 && x[j-1] > a)
         {
            // Shift left value—x[j-1]—one position to its right —
            // x[j].
            x[j] = x[j-1];
            // Update insert position to shifted value’s original position
            // (one position to the left).
            j—;
         }
         // Insert a at insert position (which is either the initial insert
         // position or the final insert position), where a is greater than
         // or equal to all values to its left.
         x[j] = a;
      }

      assert isSorted(x): array not sorted”;
   }
}

Listing. 4는 삽입 정렬 알고리즘을 sort()사용하여 정수 값의 배열을 정렬하는 도우미 메소드를 보여줍니다 .  I’ve used assert to check the postcondition of x being sorted before sort() returns to its caller. 나는 assert를 sort()호출로 정렬되어 호출자에게 반환되기 전에  x 사후 조건을 확인하는 데 익숙 하다 .

 Listing 4의 예제는 어설션의 중요한 특성을 보여 주며, 일반적으로 실행에 비용이 많이 든다. 이러한 이유로 프로덕션 코드에서는 어설션이 일반적으로 사용되지 않습니다. Listing 4에서 isSorted()배열 전체를 스캔해야 하는데, 배열이 긴 경우 시간이 오래 걸릴 수 있다.

자바 5의 제네릭

받는 사람 또한 자바 동시성 유틸리티 제네릭, 형태 보증 된 열거, 주석, 오토 박싱과 언 박싱, 향상된 for 루프, 고정 수입, 변수 인수 및 공변 반환 형식 : (이 시리즈를 위해 2013 년 6 월에 프로파일), 자바 (5)는 여덟 새로운 언어 기능을 추가했다. 다음 두 기사에서 Java 5의 모든 기능을 다루겠습니다. 여기서는 제네릭으로 시작합니다.

Generics 는 컴파일 타임 타입 안전성을 제공하면서 유형 또는 메서드가 다양한 유형의 객체에서 작동 할 수있게 해주는 언어 기능 모음입니다. Generics java.lang.ClassCastException는 형식 안전하지 않은 코드로 인해 런타임에 throw되는 문제를 해결 합니다.

Java 클래스 라이브러리의 Generics

generics는 Java Collections Framework 에서 널리 사용되지만 독점적이지 는 않습니다. 제네릭도 포함하여, 자바의 표준 클래스 라이브러리의 다른 부분에 사용되는 java.lang.Classjava.lang.Comparablejava.lang.ThreadLocal,와 java.lang.ref.WeakReference.

제네릭을 도입하기 전에 Java 코드에서 일반적인 유형 안전성이 없음을 보여주는 다음 코드 단편을 고려하십시오.

List doubleList = new LinkedList();
doubleList.add(new Double(3.5));
Double d = (Double) doubleList.iterator().next();

위의 프로그램의 목적은 java.lang.Double목록에 개체 만 저장하는 것이지만 다른 종류의 개체가 저장되는 것을 막을 수는 없습니다. 예를 들어 개체 doubleList.add(“Hello”);를 추가하도록 지정할 수 있습니다 java.lang.String. 그러나 다른 종류의 객체를 저장할 때 최종 행의 (Double)형 변환 연산자는 객체 ClassCastException가 아닌 Double객체 와 대면 할 때 발생 합니다.

런타임 시까 지 유형 안전성의 부족이 감지되지 않기 때문에 개발자는 문제를 인식하지 못하고 발견 할 클라이언트에 남겨 둘 수 있습니다. 분명히 컴파일러가 문제를 감지하게하는 것이 낫습니다. generic은 개발자가 다음과 같이 특정 유형의 객체를 포함하는 것으로 표시하도록 컴파일러를 지원합니다.

List<Double> doubleList = new LinkedList<Double>();
doubleList.add(new Double(3.5));
Double d = doubleList.iterator().next();

List<Double>이제 " Listof Double"를 읽습니다 . List는 실제 객체를 만들 때 지정 List<E>되는 Double유형 인수 를 사용하는 일반 인터페이스 입니다. 컴파일러는 이제 개체를 목록에 추가 할 때 형식 정확성을 적용 할 수 있습니다. 예를 들어 목록 Double만 저장할 수 있습니다 . 이 강요는 (Double)캐스트 의 필요성을 제거합니다 .

제네릭 형식 검색

일반적인 타입은 비아 유형 파라미터의 세트를 도입 클래스 또는 인터페이스 가형 파라미터리스트 각괄호 한 쌍의 입력 매개 변수 이름 쉼표로 구분이다. 일반 유형은 다음 구문을 준수합니다.

class identifier<formalTypeParameterList>
{
   // class body
}

interface identifier<formalTypeParameterList>
{
   // interface body
} 

Java Collections Framework에서는 제네릭 형식 및 해당 매개 변수 목록의 많은 예제를 제공합니다. 예를 들어 java.util.Set <E>는 형식 형 매개 변수 목록으로 <E>를 사용하고 E를이 목록의 단독 형 매개 변수로 사용하는 제네릭 형식입니다. java.util.Map <K, V>는 또 다른 예입니다.

이름 지정 매개 변수

Java 프로그래밍 규약에 따라 형식 매개 변수 이름은 요소의 경우 E, 키의 경우 K, 값의 경우 V, 유형의 T와 같이 대문자로 구성됩니다. 가능하다면 "P"와 같은 의미 없는 이름을 사용하지 마십시오.
java.util.List <E>는 요소 목록을 의미하지만 List <P>가 의미하는 바는 무엇입니까?

매개 변수화 된 유형은 제네릭 유형의 유형 매개 변수가 실제 유형 인수 (유형 이름)로 대체되는 일반 유형 인스턴스입니다. 예를 들어, Set <String>은 매개 변수화 된 유형이고 String은 유형 매개 변수 E를 대체하는 실제 유형 인수입니다. 

728x90
반응형