자바의 인터페이스

Java 인터페이스는 클래스와 다르며 Java 프로그램에서 특수 속성을 사용하는 방법을 아는 것이 중요합니다. 이 튜토리얼에서는 클래스와 인터페이스의 차이점을 소개 한 다음 Java 인터페이스를 선언, 구현 및 확장하는 방법을 보여주는 예제를 안내합니다.

또한 기본 및 정적 메서드를 추가하여 Java 8에서, 그리고 새로운 private 메서드를 사용하여 Java 9에서 인터페이스가 어떻게 발전했는지 배우게됩니다. 이러한 추가 기능은 숙련 된 개발자에게 인터페이스를 더욱 유용하게 만듭니다. 안타깝게도 클래스와 인터페이스 사이의 경계를 모호하게하여 인터페이스 프로그래밍을 Java 초보자에게 더욱 혼란스럽게 만듭니다.

다운로드 코드 받기이 자습서에서 예제 응용 프로그램의 소스 코드를 다운로드합니다. JavaWorld를 위해 Jeff Friesen이 만들었습니다.

Java 인터페이스 란 무엇입니까?

인터페이스는 두 개의 시스템이 만나는 지점과 상호 작용이다. 예를 들어, 자동 판매기 인터페이스를 사용하여 항목을 선택하고 비용을 지불하고 음식 또는 음료 항목을받을 수 있습니다. 프로그래밍 관점에서 인터페이스는 소프트웨어 구성 요소 사이에 위치합니다. 메서드 헤더 (메서드 이름, 매개 변수 목록 등) 인터페이스가 메서드를 호출하는 외부 코드와 호출의 결과로 실행될 메서드 내의 코드 사이에 있다고 가정합니다. 예를 들면 다음과 같습니다.

System.out.println(average(10, 15)); double average(double x, double y) // interface between average(10, 15) call and return (x + y) / 2; { return (x + y) / 2; }

Java 초보자에게 종종 혼란스러운 점은 클래스에도 인터페이스가 있다는 것입니다. Java 101 : Java의 클래스 및 객체에서 설명했듯이 인터페이스는 외부에있는 코드에 액세스 할 수있는 클래스의 일부입니다. 클래스의 인터페이스는 메서드, 필드, 생성자 및 기타 항목의 일부 조합으로 구성됩니다. 목록 1을 고려하십시오.

Listing 1. Account 클래스와 인터페이스

class Account { private String name; private long amount; Account(String name, long amount) { this.name = name; setAmount(amount); } void deposit(long amount) { this.amount += amount; } String getName() { return name; } long getAmount() { return amount; } void setAmount(long amount) { this.amount = amount; } }

Account(String name, long amount)생성자와는 void deposit(long amount), String getName(), long getAmount(), 및 void setAmount(long amount)방법은 형성 Account클래스의 인터페이스를 : 그들은 외부 코드에 액세스 할 수 있습니다. private String name;private long amount;필드는 액세스 할 수 없습니다.

Java 인터페이스에 대한 추가 정보

Java 프로그램의 인터페이스로 무엇을 할 수 있습니까? Java 인터페이스에서 Jeff의 6 가지 역할에 대한 개요를 확인하십시오.

메서드의 인터페이스를 지원하는 메서드의 코드와 클래스의 인터페이스 (예 : private 필드)를 지원하는 클래스 부분을 메서드 또는 클래스의 구현이라고 합니다. 구현은 진화하는 요구 사항을 충족하도록 변경 될 수 있도록 외부 코드에서 숨겨져 야합니다.

구현이 노출되면 소프트웨어 구성 요소 간의 상호 종속성이 발생할 수 있습니다. 예를 들어 메서드 코드는 외부 변수에 의존 할 수 있으며 클래스의 사용자는 숨겨야하는 필드에 의존하게 될 수 있습니다. 이러한 결합 은 구현을 발전시켜야 할 때 문제를 일으킬 수 있습니다 (아마도 노출 된 필드를 제거해야 함).

Java 개발자는 인터페이스 언어 기능을 사용하여 클래스 인터페이스를 추상화하므로 사용자로부터 클래스를 분리 합니다. 클래스 대신 Java 인터페이스에 초점을 맞추면 소스 코드에서 클래스 이름에 대한 참조 수를 최소화 할 수 있습니다. 이렇게하면 소프트웨어가 성숙함에 따라 한 클래스에서 다른 클래스로 (성능 향상을 위해) 쉽게 변경할 수 있습니다. 다음은 그 예입니다.

List names = new ArrayList() void print(List names) { // ... }

이 예제는 names문자열 이름 목록을 저장하는 필드를 선언하고 초기화 합니다. 이 예제는 또한 print()문자열 목록의 내용을 인쇄 하는 방법 (아마도 한 줄에 하나의 문자열)을 선언합니다 . 간결함을 위해 메서드 구현을 생략했습니다.

List객체의 순차적 컬렉션을 설명하는 Java 인터페이스입니다. Java 인터페이스 ArrayList의 배열 기반 구현을 설명하는 클래스입니다 List. ArrayList클래스 의 새 인스턴스 를 List가져와 변수에 할당 합니다 names. ( List그리고 ArrayList표준 클래스 라이브러리의 java.util패키지에 저장 됩니다.)

꺾쇠 괄호 및 제네릭

꺾쇠 괄호 ( <>)는 Java의 제네릭 기능 집합의 일부입니다. names문자열 목록 을 설명 한다는 것을 나타냅니다 (목록에는 문자열 만 저장할 수 있음). 향후 Java 101 기사에서 제네릭을 소개하겠습니다.

클라이언트 코드가와 상호 작용할 때에서 names선언하고 List에서 구현 한 메서드를 호출 합니다 ArrayList. 클라이언트 코드는 ArrayList. 결과적으로와 같은 다른 구현 클래스 LinkedList가 필요할 때 클라이언트 코드가 중단되지 않습니다.

List names = new LinkedList() // ... void print(List names) { // ... }

때문에 print()메소드 매개 변수 유형이 List,이 방법의 구현은 변화가 없습니다. 그러나 유형이 ArrayList이면 유형을으로 변경해야 LinkedList합니다. 두 클래스 모두 고유 한 메서드를 선언해야하는 경우 print()의 구현 을 크게 변경해야 할 수 있습니다 .

디커플링 List에서 ArrayList와 것은 LinkedList당신이 클래스 구현 변화에 면역의 코드를 작성할 수 있습니다. Java 인터페이스를 사용하면 구현 클래스에 의존하여 발생할 수있는 문제를 피할 수 있습니다. 이러한 분리가 Java 인터페이스를 사용하는 주된 이유입니다.

자바 인터페이스 선언

헤더와 본문으로 구성된 클래스와 유사한 구문을 준수하여 인터페이스를 선언합니다. 최소한 헤더는 키워드 interface와 인터페이스를 식별하는 이름으로 구성됩니다 . 본문은 여는 중괄호로 시작하여 닫는 중괄호로 끝납니다. 이러한 구분 기호 사이에는 상수 및 메서드 헤더 선언이 있습니다.

interface identifier { // interface body }

규칙에 따라 인터페이스 이름의 첫 글자는 대문자이고 그 이후의 글자는 소문자입니다 (예 :) Drawable. 이름이 여러 단어로 구성된 경우 각 단어의 첫 글자는 대문자입니다 (예 :) DrawableAndFillable. 이 명명 규칙을 CamelCasing이라고합니다.

목록 2는라는 인터페이스를 선언합니다 Drawable.

Listing 2. 자바 인터페이스 예제

interface Drawable { int RED = 1; int GREEN = 2; int BLUE = 3; int BLACK = 4; int WHITE = 5; void draw(int color); }

자바 표준 클래스 라이브러리의 인터페이스

명명 규칙에 따라 Java 표준 클래스 라이브러리의 많은 인터페이스는 able 접미사로 끝납니다 . 예로는 Callable, Cloneable, Comparable, Formattable, Iterable, Runnable, Serializable,와 Transferable. 그러나 접미사는 필수는 아닙니다. 표준 클래스 라이브러리는 인터페이스를 포함 CharSequence, ClipboardOwner, Collection, Executor, Future, Iterator, List, Map등 다수.

Drawable색상 상수를 식별하는 5 개의 필드를 선언합니다. 이 인터페이스는 draw()외곽선을 그리는 데 사용되는 색상을 지정하기 위해 이러한 상수 중 하나를 사용하여 호출해야하는 메서드 의 헤더도 선언합니다 . (정수 상수를 사용하는 것은 모든 정수 값을에 전달할 수 있기 때문에 좋은 생각이 아닙니다 draw(). 그러나 간단한 예에서는 충분합니다.)

필드 및 메소드 헤더 기본값

인터페이스에서 선언 된 필드는 암시 적으로 public final static입니다. 인터페이스의 메소드 헤더는 암시 적으로 public abstract.

Drawable수행 할 작업 (무언가 그리기)을 지정하지만 수행 방법은 지정하지 않는 참조 유형을 식별합니다. 구현 세부 사항은이 인터페이스를 구현하는 클래스에 맡깁니다. 이러한 클래스의 인스턴스는 자신을 그리는 방법을 알고 있기 때문에 드로어 블이라고합니다.

마커 및 태깅 인터페이스

본문이 비어있는 인터페이스를 마커 인터페이스 또는 태그 지정 인터페이스라고 합니다. 인터페이스는 메타 데이터를 클래스와 연결하기 위해서만 존재합니다. 예를 들어 Cloneable(Java의 상속, Part 2 참조)는 구현 클래스의 인스턴스가 얕게 복제 될 수 있음을 의미합니다. 때 Objectclone()호출 인스턴스의 클래스가 구현하는 방법의 검출 (을 통해 런타임 타입 식별) Cloneable, 그것은 얕게 개체를 복제합니다.

Java 인터페이스 구현

클래스는 Java의 implements키워드 다음에 쉼표로 구분 된 인터페이스 이름 목록을 클래스 헤더에 추가하고 클래스의 각 인터페이스 메소드를 코딩하여 인터페이스를 구현합니다. Listing 3은 Listing 2의 Drawable인터페이스 를 구현하는 클래스를 보여줍니다 .

Listing 3. Drawable 인터페이스를 구현하는 원

class Circle implements Drawable { private double x, y, radius; Circle(double x, double y, double radius) { this.x = x; this.y = y; this.radius = radius; } @Override public void draw(int color) { System.out.println("Circle drawn at (" + x + ", " + y + "), with radius " + radius + ", and color " + color); } double getRadius() { return radius; } double getX() { return x; } double getY() { return y; } }

목록 3의 Circle클래스는 원을 중심점과 반경으로 설명합니다. 생성자 적당한 게터 방법 제공뿐만 아니라 Circle구현 Drawable추가하여 인터페이스를 implements Drawable받는 Circle헤더 및 (의해 표시된 바와 오버라이드 @Override주석) Drawables '에 draw()있어서 헤더.

A : 4 개 선물 두 번째 예 리스팅 Rectangle클래스도 구현을 Drawable.

Listing 4. Rectangle 컨텍스트에서 Drawable 인터페이스 구현하기

class Rectangle implements Drawable { private double x1, y1, x2, y2; Rectangle(double x1, double y1, double x2, double y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } @Override public void draw(int color) { System.out.println("Rectangle drawn with upper-left corner at (" + x1 + ", " + y1 + ") and lower-right corner at (" + x2 + ", " + y2 + "), and color " + color); } double getX1() { return x1; } double getX2() { return x2; } double getY1() { return y1; } double getY2() { return y2; } }

Listing 4의 Rectangle클래스는 사각형을이 모양의 왼쪽 위와 오른쪽 아래 모서리를 나타내는 점 쌍으로 설명합니다. 와 같이 Circle, Rectangle생성자 적당한 게터 방법을 제공하고, 또한 구현하는 Drawable인터페이스.

인터페이스 메서드 헤더 재정의

인터페이스 절 abstract을 포함 implements하지만 인터페이스의 모든 메서드 헤더를 재정의하지 않는 비 클래스 를 컴파일하려고하면 컴파일러에서 오류를보고합니다 .

An interface type's data values are the objects whose classes implement the interface and whose behaviors are those specified by the interface's method headers. This fact implies that you can assign an object's reference to a variable of the interface type, provided that the object's class implements the interface. Listing 5 demonstrates.

Listing 5. Aliasing Circle and Rectangle objects as Drawables

class Draw { public static void main(String[] args) { Drawable[] drawables = new Drawable[] { new Circle(10, 20, 15), new Circle(30, 20, 10), new Rectangle(5, 8, 8, 9) }; for (int i = 0; i < drawables.length; i++) drawables[i].draw(Drawable.RED); } }

Because Circle and Rectangle implement Drawable, Circle and Rectangle objects have Drawable type in addition to their class types. Therefore, it's legal to store each object's reference in an array of Drawables. A loop iterates over this array, invoking each Drawable object's draw() method to draw a circle or a rectangle.

Assuming that Listing 2 is stored in a Drawable.java source file, which is in the same directory as the Circle.java, Rectangle.java, and Draw.java source files (which respectively store Listing 3, Listing 4, and Listing 5), compile these source files via either of the following command lines:

javac Draw.java javac *.java

Run the Draw application as follows:

java Draw

You should observe the following output:

Circle drawn at (10.0, 20.0), with radius 15.0, and color 1 Circle drawn at (30.0, 20.0), with radius 10.0, and color 1 Rectangle drawn with upper-left corner at (5.0, 8.0) and lower-right corner at (8.0, 9.0), and color 1

Note that you could also generate the same output by specifying the following main() method:

public static void main(String[] args) { Circle c = new Circle(10, 20, 15); c.draw(Drawable.RED); c = new Circle(30, 20, 10); c.draw(Drawable.RED); Rectangle r = new Rectangle(5, 8, 8, 9); r.draw(Drawable.RED); }

보시다시피 각 개체의 draw()메서드 를 반복적으로 호출하는 것은 지루합니다 . 또한 이렇게하면 Draw의 클래스 파일에 추가 바이트 코드가 추가 됩니다. 생각으로 Circle하고 RectangleDrawable의, 당신은 배열과 코드를 단순화하는 간단한 루프를 활용할 수 있습니다. 이것은 클래스보다 인터페이스를 선호하도록 코드를 설계함으로써 얻을 수있는 추가적인 이점입니다.

주의!