자바 다중 상속


168

Java의 다중 상속 문제를 해결하는 방법을 완전히 이해하기 위해 명확히해야 할 고전적인 질문이 있습니다.

I 클래스가 있다고 가정하자 Animal이 하위 클래스가 Bird그리고 Horse내가 클래스 만들 필요가 Pegasus에서 확장 Bird하고 Horse있기 때문에이 Pegasus새와 말 모두이다.

이것이 고전적인 다이아몬드 문제라고 생각합니다. 내가 이것을 해결하는 고전적인 방법을 이해할 수있는 것에서 Animal, BirdHorse클래스를 인터페이스로 만들고 구현 Pegasus하는 것입니다.

새와 말의 개체를 만들 수있는 문제를 해결하는 다른 방법이 있는지 궁금합니다. 동물을 만들 수있는 방법이 있다면 훌륭하지만 꼭 필요한 것은 아닙니다.


6
클래스를 수동으로 만들고 멤버로 상속 할 수 있다고 생각합니다 (상속 대신 구성). 프록시 ( docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html ) 클래스를 사용하면 인터페이스도 필요하지만 옵션 일 수 있습니다.
Gábor Bakos

4
@RAM은 Bird를 확장해서는 안되며 비행을 할 수있는 행동을해야합니다. : D 문제 해결
Yogesh

11
바로 그거죠. 인터페이스 CanFly는 어떻습니까. :-)
놀란 코코넛

28
나는 이것이 잘못된 접근법이라고 생각합니다. 당신은 동물이 있습니다-말, 새. 그리고 당신은-플라잉, 육식 동물 속성이 있습니다. 페가수스는 말이 아닙니다. 새는 날 수있는 말입니다. 속성은 인터페이스에 있어야합니다. 그래서 public class Pegasus extends Horse implements Flying.
거미 보리스

12
나는 당신이 왜 그것이 틀렸다고 생각하고 생물학의 규칙을 고수하지 않고 당신의 관심사를 고맙게 생각하지만 은행과 실제로 관련이있는 프로그램을 만들어야하는데 이것이 나에게 가장 좋은 접근법이라고 생각합니다. 규칙에 위배되는 실제 문제를 게시하고 싶지 않았으므로 예제를 약간 변경했습니다. 그래도 감사합니다 ...
Sheli

답변:


115

public interface Equidae말이나 public interface Avialae새 와 같은 동물 클래스 (생물학적 의미의 클래스)에 대한 인터페이스를 만들 수 있습니다 (생물학자가 아니므로 용어가 틀릴 수 있습니다).

그런 다음 여전히

public class Bird implements Avialae {
}

public class Horse implements Equidae {}

그리고 또한

public class Pegasus implements Avialae, Equidae {}

의견에서 추가 :

중복 코드를 줄이기 위해 구현하려는 동물의 공통 코드가 대부분 포함 된 추상 클래스를 만들 수 있습니다.

public abstract class AbstractHorse implements Equidae {}

public class Horse extends AbstractHorse {}

public class Pegasus extends AbstractHorse implements Avialae {}

최신 정보

하나 더 자세하게 추가하고 싶습니다. Brian 이 말했듯 이 이것은 OP가 이미 알고있는 것입니다.

그러나 인터페이스에 대한 "다중 상속"문제를 무시하고 이미 구체적인 유형 (예 : Bird)을 나타내는 인터페이스를 사용하지 않는 것이 좋습니다 (기타 참조). 오리 타이핑도 좋습니다.하지만 생물학적 의미의 조류 인 Avialae). 또한 대문자 'I'로 시작하는 인터페이스 이름 (예 : IBird인터페이스가 필요한 이유에 대해서는 아무 것도 알려주지 않음) 을 사용하지 않는 것이 좋습니다 . 그것은 질문과 다른 점입니다. 인터페이스를 사용하여 상속 계층을 구성하고, 유용한 경우 추상 클래스를 사용하고, 필요한 경우 구체적인 클래스를 구현하고 필요한 경우 위임을 사용하십시오.


9
OP가 Q에서 할 수 있다고 알고있는 것과 정확히 같은 것입니다.
Brian Roach

4
Pegasus는 이미 말 (비행)이므로 말을 확장하고 Avialae를 구현하면 더 많은 코드를 재사용 할 수 있다고 생각합니다.
Pablo Lozano

8
나는 아직 페가수스를 보지 못했다. 그러나이 경우에는 AbstractHorseZebra를 사용하여 Zebra 또는 다른 말과 같은 동물을 만드는 데 사용할 수 있습니다.
Moritz Petersen

5
@MoritzPetersen 추상화를 재사용하고 의미있는 이름을 지정하려면 AbstractEquidae보다 적합 할 것 AbstractHorse입니다. 얼룩말이 추상적 인 말을 뻗는 것이 이상 할 것입니다. 그건 그렇고 좋은 대답입니다.
afsantos

3
오리 타이핑을 구현하려면 Duck클래스를 구현 Avialae해야합니까?
mgarciaisaia

88

객체를 결합하는 데는 두 가지 기본 접근 방식이 있습니다.

  • 첫 번째는 상속 입니다. 상속의 한계를 이미 식별 했으므로 여기서 필요한 것을 수행 할 수 없음을 의미합니다.
  • 두 번째는 Composition 입니다. 상속이 실패 했으므로 구성을 사용해야합니다.

이것이 작동하는 방식은 Animal 객체가 있다는 것입니다. 그런 다음 해당 개체 내에 필요한 속성과 동작을 제공하는 추가 개체를 추가합니다.

예를 들면 다음과 같습니다.

  • 조류는 동물 구현 IFlier 연장
  • 확장 동물 구현 IHerbivore, IQuadruped
  • 페가수스 확장 동물 구현을 IHerbivore, IQuadruped, IFlier

이제 IFlier다음과 같이 보입니다.

 interface IFlier {
     Flier getFlier();
 }

따라서 Bird다음과 같습니다

 class Bird extends Animal implements IFlier {
      Flier flier = new Flier();
      public Flier getFlier() { return flier; }
 }

이제 당신은 상속의 모든 장점을 가지고 있습니다. 코드를 재사용 할 수 있습니다. IFliers 컬렉션을 가질 수 있고 다형성 등의 다른 모든 장점을 사용할 수 있습니다.

그러나 컴포지션의 모든 유연성도 있습니다. Animal각 비트의 설정 방법에 대해 필요한만큼의 제어를 통해 각 유형에 원하는만큼 다양한 인터페이스와 복합 백킹 클래스를 적용 할 수 있습니다 .

구성에 대한 전략 패턴 대안 적 접근

수행하는 작업과 방법에 따른 대안은 Animal기본 클래스에 여러 가지 동작 목록을 유지하기 위해 내부 컬렉션을 포함시키는 것입니다. 이 경우 전략 패턴에 더 가까운 것을 사용하게됩니다. 그것은 코드 단순화 측면에서 이점을 제공하지만 (예 : 또는 Horse에 대해 아무것도 알 필요가 없습니다 ) 인터페이스 접근 방식을 수행하지 않으면 다형성 등의 많은 이점을 잃게됩니다.QuadrupedHerbivore


Java에서 사용하기에 자연스럽지 않지만 (변환기 메소드 등을 사용해야 함) 비슷한 대안이 유형 클래스를 사용하고있을 수도 있지만,이 소개는 아이디어를 얻는 데 유용 할 수 있습니다. typeclassopedia.bitbucket.org
Gábor Bakos

1
이것은 허용 된 답변에서 권장되는 접근법보다 훨씬 나은 솔루션입니다. 예를 들어, 나중에 Bird 인터페이스에 "부리 색상"을 추가하고 싶다면 문제가 있습니다. 페가수스는 말과 새의 요소를 취하지 만 완전히 말이나 새가 아닌 합성물입니다. 이 시나리오에서는 컴포지션을 사용하는 것이 좋습니다.
JDB는 여전히 Monica를

그러나 getFlier()모든 종류의 새에 대해 다시 구현해야합니다.
SMUsamaShah

1
@ LifeH2O 공유 기능 블록으로 분할 한 다음 제공합니다. 즉, 당신이있을 MathsTeacher하고 EnglishTeacher모두 상속 Teacher, ChemicalEngineer, MaterialsEngineer등 상속 Engineer. Teacher그리고 Engineer둘 다 구현 Component합니다. 은 Person다음 단지의 목록이 Component들, 당신은 그들에게 바로 제공 할 수 있습니다 Component그것에 대해들 Person. 즉 person.getComponent(Teacher.class), person.getComponent(MathsTeacher.class)등.
Tim B

1
이것이 가장 좋은 대답입니다. 경험상 상속은 "is"를 나타내고 인터페이스는 "can"을 나타냅니다. 페가수스는 비행하고 걷는 동물입니다. 새는 날 수있는 동물입니다. 말은 걸을 수있는 동물입니다.
Robear

43

나는 바보 같은 생각을 가지고있다 :

public class Pegasus {
    private Horse horseFeatures; 
    private Bird birdFeatures; 

   public Pegasus(Horse horse, Bird bird) {
     this.horseFeatures = horse;
     this.birdFeatures = bird;
   }

  public void jump() {
    horseFeatures.jump();
  }

  public void fly() {
    birdFeatures.fly();
  }
}

24
그것은 작동하지만 Pegasus는 말이있는 것처럼 보이기 때문에 그런 접근 방식 (Wrappper)을 좋아하지 않습니다.
Pablo Lozano

감사합니다. 아이디어를 게시하는 데 도움을 줄 수 없었습니다. 바보 왜 ... 내가 좀 바보 알고,하지만 난 보지 못했어요
파벨 Janicek에게

3
Tim B의 대답에 따른 구성 방식의 정제되지 않은 버전과 거의 같습니다.
Stephan

1
말과 페가수스에 의해 구현되는 "점프"방법을 사용하는 IJumps와 버드와 페가수스에 의해 구현되는 "플라이"방법을 사용하는 IFlies와 같은 것이 있어야합니다.
MatsT

2
@Pablo no, Pegasus HAS horseFeatures 및 HAS birdFeatures. 어수선한 클래스 생성, 적절한 Java 솔루션과 달리 코드를 단순하게 유지하기 때문에 +1로 답하십시오.
JaneGoodall

25

오리 타이핑 의 개념을 제안해도 될까요?

페가수스가 새와 말 인터페이스를 확장시키는 경향이 있지만 오리 타이핑은 실제로 행동을 상속해야한다고 제안합니다 . 의견에서 이미 언급했듯이 페가수스는 새가 아니라 날 수 있습니다. 따라서 페가수스는 오히려- Flyable인터페이스를 상속 받아- 인터페이스라고 말해야합니다 Gallopable.

이런 종류의 개념은 전략 패턴 에서 활용됩니다 . 주어진 예는 실제로 오리 상속이 방법을 보여줍니다 FlyBehaviour그리고 QuackBehaviour아직도, 오리가있을 수 있습니다 예를 들어 RubberDuck, 비행 할 수있다. 그들은 또한 Duck확장을 한 Bird클래스 로 만들 수 있었지만 모든 Duck사람들은 심지어 가난한 사람들조차도 날 수 있기 때문에 약간의 유연성을 포기했을 것 RubberDuck입니다.


19

기술적으로 말하면 한 번에 하나의 클래스 만 확장하고 여러 인터페이스를 구현할 수 있지만 소프트웨어 엔지니어링을 할 때는 일반적으로 대답 할 수없는 문제 별 솔루션을 제안합니다. 그건 그렇고, OO 관행 은 콘크리트 클래스를 확장 하지 않고 원치 않는 상속 동작을 방지하기 위해 추상 클래스 만 확장하는 것입니다. "동물"과 같은 동물 개체는 없으며 콘크리트 동물 만 사용하는 것은 없습니다.


13

2014 년 2 월 현재 개발 단계에있는 Java 8에서는 기본 메소드 를 사용 하여 일종의 C ++와 유사한 다중 상속을 달성 할 수 있습니다 . 공식 문서보다 작업을 시작하기에 더 쉬운 몇 가지 예를 보여주는 이 자습서 를 살펴볼 수도 있습니다 .


1
Bird와 Horse 모두 ​​기본 메소드를 가지고 있다면 여전히 다이아몬드 문제가 발생하며 Pegasus 클래스에서 별도로 구현해야합니다 (또는 컴파일러 오류가 발생 함).
Mikkel Løkke

@Mikkel Løkke : Pegasus 클래스는 모호한 메소드에 대한 재정의를 정의해야하지만 수퍼 메소드 (또는 선택된 순서로)에 위임하여 구현할 수 있습니다.
Holger

12

말이 반 문을 넘을 수 없으므로 말을 반 문으로 안정되게 유지하는 것이 안전합니다. 따라서 나는 말 유형의 모든 품목을 받아들이고 반문으로 안정된 말 주택 서비스를 설정했습니다.

말처럼 날 수있는 동물 같은 말입니까?

다중 상속에 대해 많이 생각했지만 15 년 이상 프로그래밍을 했으므로 더 이상 다중 상속 구현에 신경 쓰지 않습니다.

여러 상속을 향한 디자인에 대처하려고 할 때 종종 문제 영역을 이해하지 못했음을 발표했습니다.

또는

오리처럼 보이고 오리처럼 찌르지 만 배터리가 필요한 경우 추상화가 잘못되었을 수 있습니다 .


비유를 올바르게 읽으면 처음에는 인터페이스가 디자인 문제를 해결할 수 있으므로 인터페이스가 훌륭해 보입니다. 예를 들어 인터페이스를 통해 클래스를 강제 실행하여 다른 사람의 API를 사용할 수 있습니다. 그러나 몇 년이 지난 후에는 문제가 처음부터 나쁜 디자인이라는 것을 깨달았습니다.
CS

8

Java에는 다중 상속이 없으므로 다중 상속 문제가 없습니다. 이것은 실제 다중 상속 문제 (다이아몬드 문제)를 해결하기 위해 의도적으로 설계된 것입니다.

문제를 완화하기위한 다양한 전략이 있습니다. 가장 즉각적으로 달성 할 수있는 것은 Pavel이 제안하는 Composite 객체입니다 (본질적으로 C ++에서이를 처리하는 방법). C3 선형화 (또는 이와 유사한)를 통한 다중 상속이 Java의 미래 카드에 있는지는 모르겠지만 의심합니다.

당신의 질문이 학문적이라면, 올바른 해결책은 Bird and Horse가 더 구체적이며, Pegasus가 단순히 Bird and Horse 결합 된 것이라고 가정하는 것은 잘못된 것입니다. 페가수스는 조류와 말과 공통적 인 고유 속성을 가지고 있다고 말하는 것이 더 정확할 것입니다. 이것은 Moritz의 답변이 지적한대로 충분히 모델링 될 수 있습니다.


6

나는 그것이 당신의 필요와 동물 클래스가 코드에서 어떻게 사용되는지에 달려 있다고 생각합니다.

Pegasus 클래스 내에서 Horse and Bird 구현의 메소드와 기능을 사용하려면 Pegasus를 Bird와 Horse 의 구성 으로 구현할 수 있습니다 .

public class Animals {

    public interface Animal{
        public int getNumberOfLegs();
        public boolean canFly();
        public boolean canBeRidden();
    }

    public interface Bird extends Animal{
        public void doSomeBirdThing();
    }
    public interface Horse extends Animal{
        public void doSomeHorseThing();
    }
    public interface Pegasus extends Bird,Horse{

    }

    public abstract class AnimalImpl implements Animal{
        private final int numberOfLegs;

        public AnimalImpl(int numberOfLegs) {
            super();
            this.numberOfLegs = numberOfLegs;
        }

        @Override
        public int getNumberOfLegs() {
            return numberOfLegs;
        }
    }

    public class BirdImpl extends AnimalImpl implements Bird{

        public BirdImpl() {
            super(2);
        }

        @Override
        public boolean canFly() {
            return true;
        }

        @Override
        public boolean canBeRidden() {
            return false;
        }

        @Override
        public void doSomeBirdThing() {
            System.out.println("doing some bird thing...");
        }

    }

    public class HorseImpl extends AnimalImpl implements Horse{

        public HorseImpl() {
            super(4);
        }

        @Override
        public boolean canFly() {
            return false;
        }

        @Override
        public boolean canBeRidden() {
            return true;
        }

        @Override
        public void doSomeHorseThing() {
            System.out.println("doing some horse thing...");
        }

    }

    public class PegasusImpl implements Pegasus{

        private final Horse horse = new HorseImpl();
        private final Bird bird = new BirdImpl();


        @Override
        public void doSomeBirdThing() {
            bird.doSomeBirdThing();
        }

        @Override
        public int getNumberOfLegs() {
            return horse.getNumberOfLegs();
        }

        @Override
        public void doSomeHorseThing() {
            horse.doSomeHorseThing();
        }


        @Override
        public boolean canFly() {
            return true;
        }

        @Override
        public boolean canBeRidden() {
            return true;
        }
    }
}

또 다른 가능성은 엔터티 구성 요소 시스템 동물을 정의하기 위해 상속 대신 접근법을 사용하는 것입니다. 물론 이것은 동물의 개별 Java 클래스를 가지지 않고 구성 요소에 의해서만 정의된다는 것을 의미합니다.

Entity-Component-System 접근법의 일부 의사 코드는 다음과 같습니다.

public void createHorse(Entity entity){
    entity.setComponent(NUMER_OF_LEGS, 4);
    entity.setComponent(CAN_FLY, false);
    entity.setComponent(CAN_BE_RIDDEN, true);
    entity.setComponent(SOME_HORSE_FUNCTIONALITY, new HorseFunction());
}

public void createBird(Entity entity){
    entity.setComponent(NUMER_OF_LEGS, 2);
    entity.setComponent(CAN_FLY, true);
    entity.setComponent(CAN_BE_RIDDEN, false);
    entity.setComponent(SOME_BIRD_FUNCTIONALITY, new BirdFunction());
}

public void createPegasus(Entity entity){
    createHorse(entity);
    createBird(entity);
    entity.setComponent(CAN_BE_RIDDEN, true);
}

4

인터페이스 계층 구조를 가지고 선택한 인터페이스에서 클래스를 확장 할 수 있습니다.

public interface IAnimal {
}

public interface IBird implements IAnimal {
}

public  interface IHorse implements IAnimal {
}

public interface IPegasus implements IBird,IHorse{
}

그런 다음 특정 인터페이스를 확장하여 필요에 따라 클래스를 정의하십시오.

public class Bird implements IBird {
}

public class Horse implements IHorse{
}

public class Pegasus implements IPegasus {
}

1
아니면 그냥 할 수 있습니다 공공 클래스 페가수스 동물 이물질 말, 새 확장
배트맨

OP는 이미이 솔루션을 알고 있으며이를 대체 할 수있는 방법을 찾고 있습니다.
Yogesh

@Batman은 물론 그가 할 수 있지만 계층을 확장하려면이 접근 방식을 따라야합니다
richardtz

IBirdIHorse구현해야합니다 IAnimal대신Animal
oliholz

@ Yogesh, 당신이 맞아요. 나는 그가 언급 한 곳을 간과했습니다. "초보자"로서 지금해야 할 일, 답을 삭제하거나 거기에 남겨 두겠습니까?, 감사합니다.
richardtz

4

Ehm, 당신의 클래스는 오직 하나의 서브 클래스 일 수 있지만, 원하는만큼 많은 인터페이스를 구현할 수 있습니다.

페가수스는 사실 말 (특별한 말의 "기술") 인 날 수있는 말 (말의 특별한 경우)입니다. 반면 페가수스는 새이며 걸을 수 있고 다리가 4 개입니다. 모두 코드 작성이 더 쉬운 방법에 달려 있습니다.

귀하의 경우처럼 다음과 같이 말할 수 있습니다.

abstract class Animal {
   private Integer hp = 0; 
   public void eat() { 
      hp++; 
   }
}
interface AirCompatible { 
   public void fly(); 
}
class Bird extends Animal implements AirCompatible { 
   @Override
   public void fly() {  
       //Do something useful
   }
} 
class Horse extends Animal {
   @Override
   public void eat() { 
      hp+=2; 
   }

}
class Pegasus extends Horse implements AirCompatible {
   //now every time when your Pegasus eats, will receive +2 hp  
   @Override
   public void fly() {  
       //Do something useful
   }
}

3

인터페이스는 다중 상속을 시뮬레이트하지 않습니다. Java 제작자는 다중 상속이 잘못되었다고 생각하므로 Java에는 그런 것이 없습니다.

두 클래스의 기능을 하나의 객체 구성으로 결합하려는 경우. 즉

public class Main {
    private Component1 component1 = new Component1();    
    private Component2 component2 = new Component2();
}

또한 특정 메소드를 노출하려면 해당 메소드를 정의하고 해당 제어기에 호출을 위임하도록하십시오.

인터페이스가 유용 할 수 있습니다. Component1인터페이스 Interface1Component2구현을 구현하는 Interface2경우 다음을 정의 할 수 있습니다.

class Main implements Interface1, Interface2

컨텍스트가 허용하는 위치에서 개체를 서로 바꾸어 사용할 수 있습니다.

내 견해로는 다이아몬드 문제에 빠질 수 없습니다.


직접 메모리 포인터, 부호없는 유형 및 연산자 오버로드가 잘못되지 않은 것과 같은 방식으로 잘못되지는 않습니다. 작업을 완료 할 필요는 없습니다. 자바는 손쉬운 언어로 설계되었습니다. 물건을 만들어 내고 최선을 다하지 마십시오. 이것은 추측이 아닌 지식의 분야입니다.
Gimby


3
  1. 정의 인터페이스 기능을 정의. 여러 기능에 대해 여러 인터페이스를 정의 할 수 있습니다. 이러한 기능은 특정 Animal 또는 Bird 로 구현할 수 있습니다 .
  2. 비 정적 데이터와 비공개 데이터 / 메소드를 공유하여 클래스 간 관계를 설정 하려면 상속 을 사용하십시오 .
  3. Decorator_pattern 을 사용 하여 기능을 동적으로 추가 하십시오 . 이를 통해 상속 클래스 및 조합 수를 줄일 수 있습니다.

더 나은 이해를 위해 아래 예를 살펴보십시오.

데코레이터 패턴은 언제 사용합니까?


2

복잡성을 줄이고 언어를 단순화하기 위해 Java에서는 다중 상속이 지원되지 않습니다.

A, B 및 C가 세 가지 클래스 인 시나리오를 고려하십시오. C 클래스는 A 및 B 클래스를 상속합니다. A 클래스와 B 클래스가 동일한 메소드를 가지고 있고 하위 클래스 객체에서 호출하면 A 또는 B 클래스의 메소드를 호출하는 것이 모호합니다.

컴파일 시간 오류는 런타임 오류보다 낫기 때문에 2 개의 클래스를 상속하면 java는 컴파일 시간 오류를 렌더링합니다. 따라서 동일한 방법 또는 다른 방법을 사용하더라도 컴파일 시간 오류가 발생합니다.

class A {  
    void msg() {
        System.out.println("From A");
    }  
}

class B {  
    void msg() {
        System.out.println("From B");
    }  
}

class C extends A,B { // suppose if this was possible
    public static void main(String[] args) {  
        C obj = new C();  
        obj.msg(); // which msg() method would be invoked?  
    }
} 

2

자바에서 다중 상속 문제를 해결하기 위해 → 인터페이스 사용

Mr. KVR의 J2EE (핵심 Java) 노트 51 페이지

제 27 일

  1. 인터페이스는 기본적으로 사용자 정의 데이터 형식을 개발하는 데 사용됩니다.
  2. 인터페이스와 관련하여 다중 상속 개념을 달성 할 수 있습니다.
  3. 인터페이스를 사용하면 다형성, 동적 바인딩 개념을 달성 할 수 있으므로 메모리 공간과 실행 시간의 순서로 JAVA 프로그램의 성능을 향상시킬 수 있습니다.

인터페이스는 순수하게 정의되지 않은 메소드의 콜렉션을 포함하는 구성이거나 인터페이스는 순수하게 추상적 인 메소드의 콜렉션입니다.

[...]

일-28 :

인터페이스의 기능을 클래스에 재사용하기위한 구문 -1 :

[abstract] class <clsname> implements <intf 1>,<intf 2>.........<intf n>
{
    variable declaration;
    method definition or declaration;
};

위의 구문에서 clsname은 'n'개의 인터페이스에서 기능을 상속하는 클래스 이름을 나타냅니다. 'Implements'는 인터페이스의 기능을 파생 클래스로 상속하는 데 사용되는 키워드입니다.

[...]

구문 -2는 'n'개의 인터페이스를 다른 인터페이스로 상속합니다.

interface <intf 0 name> extends <intf 1>,<intf 2>.........<intf n>
{     
    variable declaration cum initialization;
    method declaration;
};

[...]

구문 -3 :

[abstract] class <derived class name> extends <base class name> implements <intf 1>,<intf 2>.........<intf n>
{
  variable declaration;
  method definition or declaration;
};
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.