IT

올바른 방법보다 인터페이스에 더 많은 것이 있습니까?

lottoking 2020. 6. 3. 08:12
반응형

올바른 방법보다 인터페이스에 더 많은 것이 있습니까?


따라서이 인터페이스가 있다고 가정 해 보겠습니다.

public interface IBox
{
   public void setSize(int size);
   public int getSize();
   public int getArea();
  //...and so on
}

그리고 그것을 구현하는 클래스가 있습니다.

public class Rectangle implements IBox
{
   private int size;
   //Methods here
}

인터페이스 IBox를 사용하려면 실제로 다음과 같은 방식으로 인스턴스를 만들 수 없습니다.

public static void main(String args[])
{
    Ibox myBox=new Ibox();
}

권리? 따라서 실제로이 작업을 수행해야합니다.

public static void main(String args[])
{
    Rectangle myBox=new Rectangle();
}

그것이 사실이라면, 인터페이스의 유일한 목적은 인터페이스를 구현하는 클래스가 인터페이스에 설명 된대로 올바른 메소드를 가지고 있는지 확인하는 것입니다. 아니면 다른 인터페이스 사용이 있습니까?


인터페이스는 코드를보다 유연하게 만드는 방법입니다. 당신이하는 일은 이것입니다 :

Ibox myBox=new Rectangle();

그런 다음 나중에 다른 종류의 상자를 사용하기로 결정한 경우 (더 나은 종류의 상자가있는 다른 라이브러리가있을 수 있음) 코드를 다음으로 전환하십시오.

Ibox myBox=new OtherKindOfBox();

일단 익숙해지면 훌륭한 (실제로 필수적인) 작업 방법을 찾을 수 있습니다.

다른 이유는 예를 들어 상자 목록을 만들고 각 상자에 대해 일부 작업을 수행하려고하지만 목록에 다른 종류의 상자를 포함시키려는 경우입니다. 각 상자에서 다음을 수행 할 수 있습니다.

myBox.close()

(IBox에 close () 메소드가 있다고 가정) 실제 myBox 클래스는 반복에있는 상자에 따라 변경됩니다.


인터페이스를 유용하게 만드는 것은 "마음을 바꾸고 나중에 다른 구현을 사용할 수 있으며 개체가 생성 된 한 곳만 변경하면된다"는 사실 아닙니다 . 문제가 아닙니다.

실제 요점은 이미 이름입니다. 모든 사람이 해당 인터페이스에서 작동하는 모든 코드를 사용하도록 구현할 수 있는 인터페이스정의합니다 . 가장 좋은 예는 다음 java.util.Collections과 같은 인터페이스에만 작동 유용한 방법 온갖 제공하는 sort()reverse()를 들어 List. 여기서 중요한 점은이 코드는 지금 정렬하거나 반대로 사용할 수 있다는 어떤 클래스가 구현하는 List인터페이스 - 단지 ArrayListLinkedList뿐만 아니라 당신이하는 방식으로 구현 될 수있는, 자신을 작성하는 것이 클래스 쓴 사람들이 java.util.Collections상상하지 않았다.

같은 방식으로 잘 알려진 인터페이스 또는 사용자가 정의한 인터페이스에서 작동하는 코드를 작성할 수 있으며 다른 사람들이 클래스를 지원하도록 요청하지 않고도 코드를 사용할 수 있습니다.

인터페이스의 또 다른 일반적인 용도는 콜백입니다. 예를 들어, java.swing.table.TableCellRenderer 는 Swing 테이블이 특정 열의 데이터를 표시하는 방법에 영향을 줄 수 있습니다. 해당 인터페이스를 구현하고 인스턴스를에 전달 JTable하면 테이블을 렌더링하는 동안 어느 시점에서 코드가 해당 작업을 수행하도록 호출됩니다.


내가 읽은 많은 용도 중 하나는 Java에서 다중 상속 사용 인터페이스가 없으면 어려운 곳입니다.

class Animal
{
void walk() { } 
....
.... //other methods and finally
void chew() { } //concentrate on this
} 

이제 다음과 같은 경우를 상상해보십시오.

class Reptile extends Animal 
{ 
//reptile specific code here
} //not a problem here

그러나,

class Bird extends Animal
{
...... //other Bird specific code
} //now Birds cannot chew so this would a problem in the sense Bird classes can also call chew() method which is unwanted

더 나은 디자인은 다음과 같습니다.

class Animal
{
void walk() { } 
....
.... //other methods 
} 

Animal은 chew () 메소드를 가지고 있지 않으며 대신 다음과 같은 인터페이스에 배치됩니다.

interface Chewable {
void chew();
}

그리고 파충류 클래스가 새가 아닌 이것을 구현하도록하십시오 (조류는 씹을 수 없기 때문에).

class Reptile extends Animal implements Chewable { } 

새의 경우 :

class Bird extends Animal { }

인터페이스의 목적은 다형성 , 일명 유형 대체 입니다. 예를 들어 다음과 같은 방법이 있습니다.

public void scale(IBox b, int i) {
   b.setSize(b.getSize() * i);
}

scale메소드를 호출 할 때 IBox인터페이스 를 구현하는 유형의 값을 제공 할 수 있습니다. 즉, if RectangleSquareboth implement IBox이면 a Rectangle또는 Square어디에서든지 IBox기대할 수 있습니다.


인터페이스는 정적으로 입력 된 언어가 다형성을 지원할 수 있도록합니다. 객체 지향적 순수 주의자는 완전한 기능을 갖춘 객체 지향 언어가되기 위해서는 언어가 상속, 캡슐화, 모듈화 및 다형성을 제공해야한다고 주장합니다. 동적 유형 또는 오리 유형 언어 (Smalltalk와 같은)에서 다형성은 사소합니다. 그러나 정적으로 유형이 지정된 언어 (예 : Java 또는 C #)에서 다형성은 사소한 것이 아닙니다 (사실상, 강력한 타이핑 개념과는 상충되는 것 같습니다).

보여 드리겠습니다 :

스몰 토크와 같이 동적으로 유형이 지정된 (또는 오리 유형의) 언어에서 모든 변수는 객체에 대한 참조입니다.

|anAnimal|    
anAnimal := Pig new.
anAnimal makeNoise.

anAnimal := Cow new.
anAnimal makeNoise.

그 코드 :

  1. anAnimal이라는 지역 변수를 선언합니다 (변수의 TYPE을 지정하지 마십시오. 모든 변수는 객체에 대한 참조입니다.).
  2. "Pig"라는 클래스의 새 인스턴스를 만듭니다.
  3. 해당 Pig의 새 인스턴스를 변수 anAnimal에 지정합니다.
  4. 메시지 makeNoise를 돼지에게 보냅니다 .
  5. 소를 사용하여 모든 것을 반복하지만 돼지와 동일한 정확한 변수에 할당합니다.

동일한 Java 코드는 다음과 같습니다 (Duck과 Cow는 Animal의 하위 클래스라고 가정합니다).

Animal anAnimal = new Pig();
duck.makeNoise();

anAnimal = new Cow();
cow.makeNoise();

우리가 야채 클래스를 소개 할 때까지는 모두 훌륭합니다. 야채는 동물과 같은 행동을하지만 전부는 아닙니다. 예를 들어, 동물과 야채는 모두 자랄 수 있지만 분명히 야채는 소리를 내지 않으며 동물은 수확 할 수 없습니다.

스몰 토크에서는 다음과 같이 쓸 수 있습니다 :

|aFarmObject|
aFarmObject := Cow new.
aFarmObject grow.
aFarmObject makeNoise.

aFarmObject := Corn new.
aFarmObject grow.
aFarmObject harvest.

이것은 오리 유형이기 때문에 Smalltalk에서 완벽하게 작동합니다 (오리처럼 걷고, 오리처럼 qua 경우-오리입니다).이 경우 메시지가 객체로 전송되면 조회가 수행됩니다. 수신자의 메소드 목록 및 일치하는 메소드가 발견되면 호출됩니다. 그렇지 않은 경우 일종의 NoSuchMethodError 예외가 발생하지만 모두 런타임에 완료됩니다.

그러나 정적으로 유형이 지정된 언어 인 Java에서 변수에 어떤 유형을 할당 할 수 있습니까? 옥수수는 야채를 물려 받아 성장을 지원해야하지만 소리를 내지 않기 때문에 동물을 물려받을 수는 없습니다. 암소는 makeNoise를 지원하기 위해 동물로부터 물려 받아야하지만, 수확을 구현해서는 안되므로 야채에서는 물려받을 수 없습니다. 다중 상속이 필요한 것 같습니다 . 둘 이상의 클래스에서 상속 할 수 있습니다. 그러나 그것은 팝업이 발생하는 모든 경우 때문에 매우 어려운 언어 기능으로 판명되었습니다 (하나 이상의 병렬 수퍼 클래스가 동일한 메소드를 구현하면 어떻게됩니까? 등).

인터페이스와 함께 ...

Growable을 구현할 때마다 Animal and Vegetable 클래스를 만들면 Cow는 Animal이고 Corn은 Vegetable이라고 선언 할 수 있습니다. 우리는 또한 동물과 야채 모두 성장할 수 있다고 선언 할 수 있습니다. 이를 통해 모든 것을 키울 수 있습니다.

List<Growable> list = new ArrayList<Growable>();
list.add(new Cow());
list.add(new Corn());
list.add(new Pig());

for(Growable g : list) {
   g.grow();
}

동물 소리를 내기 위해 이렇게 할 수 있습니다.

List<Animal> list = new ArrayList<Animal>();
list.add(new Cow());
list.add(new Pig());
for(Animal a : list) {
  a.makeNoise();
}

오리 유형 언어의 장점은 정말 좋은 다형성을 얻는다는 것입니다. 클래스가 행동을 제공하기 위해해야하는 모든 것은 메소드를 제공하는 것입니다. 모두가 훌륭하고 정의 된 메소드와 일치하는 메시지 만 보내는 한 모든 것이 좋습니다. 단점은 런타임까지 아래의 종류의 오류가 포착되지 않는다는 것입니다.

|aFarmObject|
aFarmObject := Corn new.
aFarmObject makeNoise. // No compiler error - not checked until runtime.

정적으로 유형이 지정된 언어는 컴파일 타임에 아래 두 가지 종류의 오류를 포착하기 때문에 "계약에 의한 프로그래밍"이 훨씬 우수합니다.

// Compiler error: Corn cannot be cast to Animal.
Animal farmObject = new Corn();  
farmObject makeNoise();

-

// Compiler error: Animal doesn't have the harvest message.
Animal farmObject = new Cow();
farmObject.harvest(); 

요약하자면 :

  1. 인터페이스 구현을 통해 객체가 수행 할 수있는 작업 유형 (상호 작용)을 지정할 수 있으며 클래스 상속을 통해 작업 수행 방법 (구현)을 지정할 수 있습니다.

  2. 인터페이스는 컴파일러 유형 검사를 희생하지 않고 "진정한"다형성의 많은 이점을 제공합니다.


일반적으로 인터페이스는 사용해야하는 인터페이스를 정의합니다 (이름에 ;-)로 표시됨). 견본


public void foo(List l) {
   ... do something
}

이제 함수 fooArrayLists, LinkedLists, ... 만 허용 합니다.

Java에서 가장 중요한 것은 여러 인터페이스를 구현할 수 있지만 ONE 클래스 만 확장 할 수 있다는 것입니다! 견본:


class Test extends Foo implements Comparable, Serializable, Formattable {
...
}
가능하지만

class Test extends Foo, Bar, Buz {
...
}
아니다!

위의 코드는 다음과 같습니다 IBox myBox = new Rectangle();.. 중요한 점은 myBox에만 IBox의 메소드 / 필드를 포함하고의 기존 메소드는 포함하지 않는 것입니다 Rectangle.


인터페이스가하는 모든 것을 이해한다고 생각하지만 인터페이스가 유용한 상황을 아직 상상하지 못하고 있습니다.

좁은 범위 내에서 (예 : 하나의 메서드 호출 내에서) 객체를 인스턴스화, 사용 및 해제하는 경우 인터페이스는 실제로 아무 것도 추가하지 않습니다. 언급했듯이 구체적인 클래스가 알려져 있습니다.

인터페이스가 유용한 곳은 오브젝트를 한 곳에서 작성하고 구현 세부 사항에 관심이없는 호출자에게 리턴해야하는 경우입니다. IBox 예제를 Shape로 변경해 봅시다. 이제 Rectangle, Circle, Triangle 등과 같은 Shape를 구현할 수 있습니다. getArea () 및 getSize () 메소드의 구현은 각 구체적인 클래스마다 완전히 다릅니다.

이제 전달 된 매개 변수에 따라 적절한 모양을 반환하는 다양한 createShape (params) 메소드가있는 팩토리를 사용할 수 있습니다. 분명히 팩토리는 어떤 유형의 쉐이프가 생성되는지 알 수 있지만 호출자에게는 없습니다. 원인지 정사각형인지 걱정할 필요가 있습니다.

이제 모양에 대해 다양한 작업을 수행해야한다고 상상해보십시오. 영역별로 정렬하고 새로운 크기로 설정 한 다음 UI에 표시해야 할 수도 있습니다. 셰이프는 모두 팩토리에서 만든 다음 Sorter, Sizer 및 Display 클래스로 매우 쉽게 전달 될 수 있습니다. 나중에 육각 클래스를 추가해야 할 경우 팩토리 이외의 것을 변경할 필요가 없습니다. 인터페이스가 없으면 다른 모양을 추가하는 것은 매우 복잡한 과정이됩니다.


넌 할 수있어

Ibox myBox = new Rectangle();

그렇게하면이 객체를 Ibox로 사용하고 실제로 신경 쓰지 않습니다 Rectangle.


왜 인터페이스인가 ??????

개로 시작합니다. 특히, 퍼그 .

퍼그에는 다양한 동작이 있습니다.

public class Pug { 
private String name;
public Pug(String n) { name = n; } 
public String getName() { return name; }  
public String bark() { return  "Arf!"; } 
public boolean hasCurlyTail() { return true; } }

그리고 당신은 래브라도를 가지고 있습니다.

public class Lab { 
private String name; 
public Lab(String n) { name = n; } 
public String getName() { return name; } 
public String bark() { return "Woof!"; } 
public boolean hasCurlyTail() { return false; } }

퍼그와 랩을 만들 수 있습니다 :

Pug pug = new Pug("Spot"); 
Lab lab = new Lab("Fido");

그리고 우리는 그들의 행동을 불러 낼 수 있습니다 :

pug.bark() -> "Arf!" 
lab.bark() -> "Woof!" 
pug.hasCurlyTail() -> true 
lab.hasCurlyTail() -> false 
pug.getName() -> "Spot"

개 사육장을 운영하고 있으며 내가 살고있는 모든 개를 추적해야한다고 가정 해 봅시다. 나는 별도의 배열에 내 퍼그와 래브라도을 저장해야합니다 :

public class Kennel { 
Pug[] pugs = new Pug[10]; 
Lab[] labs = new Lab[10];  
public void addPug(Pug p) { ... } 
public void addLab(Lab l) { ... } 
public void printDogs() { // Display names of all the dogs } }

그러나 이것은 분명히 최적이 아닙니다. 경우 좀 푸들을 수용 할 도, 나는 푸들의 배열을 추가 내 개집 정의를 변경해야합니다. 사실, 나는 개 종류마다 별도의 배열이 필요합니다.

통찰력 : 퍼그와 래브라도 (푸들)는 개 유형이며 같은 행동을 취합니다. 즉, 우리는 (이 예의 목적을 위해) 모든 개가 짖을 수 있고 이름을 가질 수 있으며 곱슬 꼬리를 가질 수도 있고 말지 않을 수도 있다고 말할 수 있습니다. 인터페이스를 사용하여 모든 개가 할 수있는 일을 정의 할 수 있지만 특정 행동 유형을 구현하기 위해 특정 유형의 개에 맡겨 두십시오. 인터페이스는 "모든 개가 할 수있는 일이 있습니다"라고 말하지만 각 행동이 어떻게 수행되는지는 말하지 않습니다.

public interface Dog 
{
public String bark(); 
public String getName(); 
public boolean hasCurlyTail(); }

그런 다음 Pug 및 Lab 클래스를 약간 변경하여 Dog 동작을 구현합니다. 퍼그는 개이고 랩은 개라고 말할 수 있습니다.

public class Pug implements Dog {
// the rest is the same as before } 

public class Lab implements Dog { 
// the rest is the same as before 
}

이전과 마찬가지로 여전히 퍼그와 랩을 인스턴스화 할 수 있지만 이제는 새로운 방법을 사용할 수 있습니다.

Dog d1 = new Pug("Spot"); 
Dog d2 = new Lab("Fido");

이것은 d1은 개일뿐만 아니라, 특히 퍼그입니다. d2는 또한 개, 특히 실험실입니다. 동작을 호출 할 수 있으며 이전과 같이 작동합니다.

d1.bark() -> "Arf!" 
d2.bark() -> "Woof!" 
d1.hasCurlyTail() -> true 
d2.hasCurlyTail() -> false 
d1.getName() -> "Spot"

추가 작업이 모두 필요한 곳입니다. Kennel 클래스가 훨씬 단순 해졌습니다. 하나의 배열과 하나의 addDog 메소드 만 필요합니다. 둘 다 개인 어떤 물건과도 잘 어울립니다. 즉, Dog 인터페이스를 구현하는 객체입니다.

public class Kennel {
Dog[] dogs = new Dog[20]; 
public void addDog(Dog d) { ... } 
public void printDogs() {
// Display names of all the dogs } }

사용 방법은 다음과 같습니다.

Kennel k = new Kennel(); 
Dog d1 = new Pug("Spot"); 
Dog d2 = new Lab("Fido"); 
k.addDog(d1); 
k.addDog(d2); 
k.printDogs();

마지막 문장은 스팟 피도

인터페이스는 인터페이스를 구현하는 모든 클래스가 공통으로 공유 할 일련의 동작을 지정할 수있는 기능을 제공합니다. 결과적으로, 어떤 종류의 특정 객체를 미리 알 필요가없고 인터페이스를 구현하는 객체 만 보유 할 변수와 컬렉션 (예 : 배열)을 정의 할 수 있습니다.


인터페이스 사용 방법의 훌륭한 예는 Collections 프레임 워크에 있습니다. 를 사용하는 함수를 작성 List하면 사용자가 a Vector또는 an ArrayList또는 a HashList또는 무엇이든 전달하는지는 중요하지 않습니다 . 또한 인터페이스 또는 인터페이스가 List필요한 모든 기능으로 전달할 수 있습니다.CollectionIterable

이것은 구현 Collections.sort(List list)방법에 관계없이 기능을 가능 하게합니다 List.


이것이 팩토리 패턴 및 기타 작성 패턴이 Java에서 그렇게 인기있는 이유 입니다. Java가 없으면 인스턴스화를 쉽게 추상화 할 수있는 즉시 사용 가능한 메커니즘을 제공하지 않습니다. 여전히 메서드에서 객체를 만들지 않는 모든 곳에서 추상화를 얻습니다 .이 코드는 대부분의 코드입니다.

따로, 나는 일반적으로 사람들에게 인터페이스 이름 지정을위한 "IRealname"메커니즘을 따르지 말 것을 권장합니다. 그것은 헝가리어 표기법의 한 발자국에 발을 들여 놓고 실제로 필요하지 않은 Windows / COM 일입니다 (Java는 이미 강력하게 유형이 지정되어 있으며 인터페이스를 갖는 요점은 가능한 한 클래스 유형과 구별 할 수 없도록하는 것입니다).


나중에 기존 클래스를 가져 와서 구현 IBox하면 모든 상자 인식 코드에서 사용할 수 있음을 잊지 마십시오 .

인터페이스의 이름이 -able 인 경우 좀 더 명확 해집니다 . 예 :

public interface Saveable {
....

public interface Printable {
....

등. (이름 지정 체계가 항상 작동하는 것은 아닙니다. 예를 들어 Boxable여기가 적절 하지는 않습니다 )


인터페이스의 유일한 목적은 인터페이스를 구현하는 클래스가 인터페이스에 설명 된대로 올바른 메소드를 가지고 있는지 확인하는 것입니다. 아니면 다른 인터페이스 사용이 있습니까?

Java 8 버전으로 도입 된 새로운 인터페이스 기능으로 답변을 업데이트하고 있습니다.

인터페이스 요약대한 오라클 설명서 페이지에서 :

인터페이스 선언은

  1. 메소드 서명
  2. 기본 방법
  3. 정적 메소드
  4. 상수 정의.

구현이있는 유일한 메소드는 기본 및 정적 메소드입니다.

인터페이스 사용 :

  1. 계약 을 정의하려면
  2. 관련이없는 클래스 를 기능과 연결 하는 것 (예 : Serializable인터페이스를 구현하는 클래스 는 해당 인터페이스를 구현하는 것 외에는 관계가있을 수도 있고 없을 수도 있음)
  3. 교환 가능한 구현 을 제공하기 위해 전략 패턴
  4. 기본 방법을 사용하면 라이브러리의 인터페이스에 새로운 기능을 추가하고 해당 인터페이스의 이전 버전 용으로 작성된 코드와 이진 호환성을 보장 할 수 있습니다
  5. 정적 메소드 를 사용하여 라이브러리에서 헬퍼 메소드 구성 (정적 메소드는 별도의 클래스가 아닌 동일한 인터페이스에서 인터페이스에 고유 한 정적 메소드를 유지할 수 있음)

Some related SE questions with respect to difference between abstract class and interface and use cases with working examples:

What is the difference between an interface and abstract class?

How should I have explained the difference between an Interface and an Abstract class?

Have a look at documentation page to understand new features added in java 8 : default methods and static methods.


The purpose of interfaces is abstraction, or decoupling from implementation.

If you introduce an abstraction in your program, you don't care about the possible implementations. You are interested in what it can do and not how, and you use an interface to express this in Java.


If you have CardboardBox and HtmlBox (both of which implement IBox), you can pass both of them to any method that accepts a IBox. Even though they are both very different and not completely interchangable, methods that don't care about "open" or "resize" can still use your classes (perhaps because they care about how many pixels are needed to display something on a screen).


Interfaces where a fetature added to java to allow multiple inheritance. The developers of Java though/realized that having multiple inheritance was a "dangerous" feature, that is why the came up with the idea of an interface.

multiple inheritance is dangerous because you might have a class like the following:


class Box{
    public int getSize(){
       return 0;
    }
    public int getArea(){
       return 1;
    }

}

class Triangle{
    public int getSize(){
       return 1;
    }
    public int getArea(){
       return 0;
    }

}

class FunckyFigure extends Box, Triable{
   // we do not implement the methods we will used the inherited ones
}

Which would be the method that should be called when we use


   FunckyFigure.GetArea(); 

All the problems are solved with interfaces, because you do know you can extend the interfaces and that they wont have classing methods... ofcourse the compiler is nice and tells you if you did not implemented a methods, but I like to think that is a side effect of a more interesting idea.


Here is my understanding of interface advantage. Correct me if I am wrong. Imagine we are developing OS and other team is developing the drivers for some devices. So we have developed an interface StorageDevice. We have two implementations of it (FDD and HDD) provided by other developers team.

Then we have a OperatingSystem class which can call interface methods such as saveData by just passing an instance of class implemented the StorageDevice interface.

The advantage here is that we don't care about the implementation of the interface. The other team will do the job by implementing the StorageDevice interface.

package mypack;

interface StorageDevice {
    void saveData (String data);
}


class FDD implements StorageDevice {
    public void saveData (String data) {
        System.out.println("Save to floppy drive! Data: "+data);
    }
}

class HDD implements StorageDevice {
    public void saveData (String data) {
        System.out.println("Save to hard disk drive! Data: "+data);
    }
}

class OperatingSystem {
    public String name;
    StorageDevice[] devices;
    public OperatingSystem(String name, StorageDevice[] devices) {

        this.name = name;
        this.devices = devices.clone();

        System.out.println("Running OS " + this.name);
        System.out.println("List with storage devices available:");
        for (StorageDevice s: devices) {
            System.out.println(s);
        }

    }

    public void saveSomeDataToStorageDevice (StorageDevice storage, String data) {
        storage.saveData(data);
    }
}

public class Main {

    public static void main(String[] args) {

        StorageDevice fdd0 = new FDD();
        StorageDevice hdd0 = new HDD();     
        StorageDevice[] devs = {fdd0, hdd0};        
        OperatingSystem os = new OperatingSystem("Linux", devs);
        os.saveSomeDataToStorageDevice(fdd0, "blah, blah, blah...");    
    }
}

참고URL : https://stackoverflow.com/questions/504904/is-there-more-to-an-interface-than-having-the-correct-methods

반응형